summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.gitignore7
-rw-r--r--CMakeLists.txt12
-rw-r--r--COPYING2
-rw-r--r--Jenkinsfile16
-rw-r--r--LICENSE.txt37
-rw-r--r--OFFICIAL-RELEASE-STEPS.md17
-rw-r--r--README.md31
-rw-r--r--RELEASE-NOTES.md35
-rw-r--r--artwork/ZeroTierIcon32x32.pngbin0 -> 7219 bytes
-rw-r--r--attic/Cluster.cpp (renamed from node/Cluster.cpp)14
-rw-r--r--attic/Cluster.hpp (renamed from node/Cluster.hpp)10
-rw-r--r--attic/ClusterDefinition.hpp (renamed from service/ClusterDefinition.hpp)12
-rw-r--r--attic/ClusterGeoIpService.cpp (renamed from service/ClusterGeoIpService.cpp)10
-rw-r--r--attic/ClusterGeoIpService.hpp (renamed from service/ClusterGeoIpService.hpp)10
-rw-r--r--attic/FCV.hpp101
-rw-r--r--attic/big-http-test/2015-11-10_01_50000.out.xzbin730360 -> 0 bytes
-rw-r--r--attic/big-http-test/2015-11-10_02_50000.out.xzbin2373664 -> 0 bytes
-rw-r--r--attic/big-http-test/2015-11-10_03_12500_ec2-east-only.out.xzbin802932 -> 0 bytes
-rw-r--r--attic/big-http-test/Dockerfile24
-rw-r--r--attic/big-http-test/README.md12
-rw-r--r--attic/big-http-test/agent.js196
-rwxr-xr-xattic/big-http-test/big-test-kill.sh9
-rwxr-xr-xattic/big-http-test/big-test-start.sh13
-rw-r--r--attic/big-http-test/crunch-results.js65
-rwxr-xr-xattic/big-http-test/docker-main.sh16
-rw-r--r--attic/big-http-test/nodesource-el.repo6
-rw-r--r--attic/big-http-test/package.json16
-rw-r--r--attic/big-http-test/server.js53
-rw-r--r--attic/cli/README.md57
-rw-r--r--attic/cli/zerotier.cpp957
-rw-r--r--attic/historic/anode/LICENSE.txt674
-rw-r--r--attic/historic/anode/config.mk.Darwin17
-rw-r--r--attic/historic/anode/config.mk.Linux17
-rw-r--r--attic/historic/anode/docs/anode_protocol.txt764
-rw-r--r--attic/historic/anode/libanode/Makefile33
-rw-r--r--attic/historic/anode/libanode/address.c98
-rw-r--r--attic/historic/anode/libanode/aes_digest.c85
-rw-r--r--attic/historic/anode/libanode/anode.h795
-rw-r--r--attic/historic/anode/libanode/errors.c52
-rw-r--r--attic/historic/anode/libanode/identity.c110
-rw-r--r--attic/historic/anode/libanode/impl/aes.c72
-rw-r--r--attic/historic/anode/libanode/impl/aes.h64
-rw-r--r--attic/historic/anode/libanode/impl/dictionary.c239
-rw-r--r--attic/historic/anode/libanode/impl/dictionary.h126
-rw-r--r--attic/historic/anode/libanode/impl/dns_txt.c93
-rw-r--r--attic/historic/anode/libanode/impl/dns_txt.h37
-rw-r--r--attic/historic/anode/libanode/impl/ec.c219
-rw-r--r--attic/historic/anode/libanode/impl/ec.h61
-rw-r--r--attic/historic/anode/libanode/impl/environment.c118
-rw-r--r--attic/historic/anode/libanode/impl/environment.h (renamed from node/NonCopyable.hpp)34
-rw-r--r--attic/historic/anode/libanode/impl/http_client.c558
-rw-r--r--attic/historic/anode/libanode/impl/http_client.h200
-rw-r--r--attic/historic/anode/libanode/impl/misc.c190
-rw-r--r--attic/historic/anode/libanode/impl/misc.h193
-rw-r--r--attic/historic/anode/libanode/impl/mutex.h34
-rw-r--r--attic/historic/anode/libanode/impl/thread.c58
-rw-r--r--attic/historic/anode/libanode/impl/thread.h65
-rw-r--r--attic/historic/anode/libanode/impl/types.h25
-rw-r--r--attic/historic/anode/libanode/network_address.c136
-rw-r--r--attic/historic/anode/libanode/secure_random.c88
-rw-r--r--attic/historic/anode/libanode/system_transport.c948
-rw-r--r--attic/historic/anode/libanode/tests/Makefile25
-rw-r--r--attic/historic/anode/libanode/tests/aes-test.c191
-rw-r--r--attic/historic/anode/libanode/tests/anode-secure_random-test.c38
-rw-r--r--attic/historic/anode/libanode/tests/anode-utils-test.c75
-rw-r--r--attic/historic/anode/libanode/tests/anode-zone-test.c47
-rw-r--r--attic/historic/anode/libanode/tests/dictionary-test.c149
-rw-r--r--attic/historic/anode/libanode/tests/ec-test.c97
-rw-r--r--attic/historic/anode/libanode/tests/environment-test.c28
-rw-r--r--attic/historic/anode/libanode/tests/http_client-test.c233
-rw-r--r--attic/historic/anode/libanode/tests/misc-test.c137
-rw-r--r--attic/historic/anode/libanode/tests/system_transport-test.c70
-rw-r--r--attic/historic/anode/libanode/uri.c185
-rw-r--r--attic/historic/anode/libanode/utils/anode-make-identity.c50
-rw-r--r--attic/historic/anode/libanode/zone.c184
-rw-r--r--attic/historic/anode/libspark/Makefile16
-rw-r--r--attic/historic/anode/libspark/experiments/FindGoodSegmentDelimiters.cpp161
-rw-r--r--attic/historic/anode/libspark/experiments/Makefile5
-rw-r--r--attic/historic/anode/libspark/streamencoder.h108
-rw-r--r--attic/historic/anode/libspark/wrapper.h66
-rw-r--r--attic/kubernetes_docs/.zerotierCliSettings (renamed from doc/ext/kubernetes/.zerotierCliSettings)0
-rw-r--r--attic/kubernetes_docs/Dockerfile (renamed from doc/ext/kubernetes/Dockerfile)0
-rw-r--r--attic/kubernetes_docs/README.md (renamed from doc/ext/kubernetes/README.md)0
-rw-r--r--attic/kubernetes_docs/entrypoint.sh (renamed from doc/ext/kubernetes/entrypoint.sh)0
-rw-r--r--attic/kubernetes_docs/server.js (renamed from doc/ext/kubernetes/server.js)0
-rw-r--r--attic/lat_lon_to_xyz.js25
-rw-r--r--attic/linux-build-farm/README.md8
-rw-r--r--attic/linux-build-farm/amazon-2016.03/x64/Dockerfile13
-rwxr-xr-xattic/linux-build-farm/build.sh69
-rw-r--r--attic/linux-build-farm/centos-6/x64/Dockerfile13
-rw-r--r--attic/linux-build-farm/centos-6/x86/Dockerfile13
-rw-r--r--attic/linux-build-farm/centos-7/x64/Dockerfile10
-rw-r--r--attic/linux-build-farm/centos-7/x86/Dockerfile22
-rw-r--r--attic/linux-build-farm/debian-jessie/x64/Dockerfile12
-rw-r--r--attic/linux-build-farm/debian-jessie/x86/Dockerfile12
-rw-r--r--attic/linux-build-farm/debian-stretch/x64/Dockerfile12
-rw-r--r--attic/linux-build-farm/debian-stretch/x86/Dockerfile12
-rw-r--r--attic/linux-build-farm/debian-wheezy/x64/Dockerfile12
-rw-r--r--attic/linux-build-farm/debian-wheezy/x86/Dockerfile15
-rw-r--r--attic/linux-build-farm/fedora-22/x64/Dockerfile10
-rw-r--r--attic/linux-build-farm/fedora-22/x86/Dockerfile19
-rwxr-xr-xattic/linux-build-farm/make-apt-repos.sh16
-rwxr-xr-xattic/linux-build-farm/make-rpm-repos.sh64
-rw-r--r--attic/linux-build-farm/ubuntu-trusty/x64/Dockerfile12
-rw-r--r--attic/linux-build-farm/ubuntu-trusty/x86/Dockerfile12
-rw-r--r--attic/linux-build-farm/ubuntu-wily/x64/Dockerfile12
-rw-r--r--attic/linux-build-farm/ubuntu-wily/x86/Dockerfile12
-rw-r--r--attic/linux-build-farm/ubuntu-xenial/x64/Dockerfile14
-rw-r--r--attic/linux-build-farm/ubuntu-xenial/x86/Dockerfile14
-rw-r--r--attic/old-controller-schema.sql112
-rwxr-xr-xattic/old-linux-installer/buildinstaller.sh134
-rw-r--r--attic/old-linux-installer/install.tmpl.sh182
-rwxr-xr-xattic/old-linux-installer/uninstall.sh76
-rw-r--r--controller/DB.cpp511
-rw-r--r--controller/DB.hpp139
-rw-r--r--controller/EmbeddedNetworkController.cpp1206
-rw-r--r--controller/EmbeddedNetworkController.hpp151
-rw-r--r--controller/FileDB.cpp150
-rw-r--r--controller/FileDB.hpp46
-rw-r--r--controller/JSONDB.cpp219
-rw-r--r--controller/JSONDB.hpp116
-rw-r--r--controller/README.md85
-rw-r--r--controller/RethinkDB.cpp488
-rw-r--r--controller/RethinkDB.hpp84
-rw-r--r--controller/migrate-sqlite/migrate.js320
-rw-r--r--controller/migrate-sqlite/package.json15
-rw-r--r--debian/changelog6
-rwxr-xr-xdebian/rules2
-rw-r--r--ext/bin/tap-windows-ndis6/certutil.exebin0 -> 903168 bytes
-rw-r--r--ext/bin/tap-windows-ndis6/x64/ZeroTierOne_NDIS6_x64.msibin1453568 -> 2056704 bytes
-rw-r--r--ext/bin/tap-windows-ndis6/x86/ZeroTierOne_NDIS6_x86.msibin1070592 -> 1673728 bytes
-rw-r--r--ext/bin/tap-windows-ndis6/zttap300.cerbin0 -> 1321 bytes
-rw-r--r--ext/ed25519-amd64-asm/batch.c94
-rw-r--r--ext/ed25519-amd64-asm/choose_t.s1565
-rw-r--r--ext/ed25519-amd64-asm/consts.s39
-rw-r--r--ext/ed25519-amd64-asm/fe25519.h64
-rw-r--r--ext/ed25519-amd64-asm/fe25519_add.s189
-rw-r--r--ext/ed25519-amd64-asm/fe25519_freeze.s322
-rw-r--r--ext/ed25519-amd64-asm/fe25519_getparity.c8
-rw-r--r--ext/ed25519-amd64-asm/fe25519_invert.c60
-rw-r--r--ext/ed25519-amd64-asm/fe25519_iseq.c14
-rw-r--r--ext/ed25519-amd64-asm/fe25519_iszero.c12
-rw-r--r--ext/ed25519-amd64-asm/fe25519_mul.s865
-rw-r--r--ext/ed25519-amd64-asm/fe25519_neg.c8
-rw-r--r--ext/ed25519-amd64-asm/fe25519_pack.c13
-rw-r--r--ext/ed25519-amd64-asm/fe25519_pow2523.c55
-rw-r--r--ext/ed25519-amd64-asm/fe25519_setint.c9
-rw-r--r--ext/ed25519-amd64-asm/fe25519_square.s639
-rw-r--r--ext/ed25519-amd64-asm/fe25519_sub.s189
-rw-r--r--ext/ed25519-amd64-asm/fe25519_unpack.c11
-rw-r--r--ext/ed25519-amd64-asm/ge25519.h95
-rw-r--r--ext/ed25519-amd64-asm/ge25519_add.c8
-rw-r--r--ext/ed25519-amd64-asm/ge25519_add_p1p1.s4554
-rw-r--r--ext/ed25519-amd64-asm/ge25519_base.c7
-rw-r--r--ext/ed25519-amd64-asm/ge25519_base_niels.data1536
-rw-r--r--ext/ed25519-amd64-asm/ge25519_base_niels_smalltables.data768
-rw-r--r--ext/ed25519-amd64-asm/ge25519_base_slide_multiples.data96
-rw-r--r--ext/ed25519-amd64-asm/ge25519_dbl_p1p1.s2975
-rw-r--r--ext/ed25519-amd64-asm/ge25519_double.c8
-rw-r--r--ext/ed25519-amd64-asm/ge25519_double_scalarmult.c102
-rw-r--r--ext/ed25519-amd64-asm/ge25519_isneutral.c9
-rw-r--r--ext/ed25519-amd64-asm/ge25519_multi_scalarmult.c102
-rw-r--r--ext/ed25519-amd64-asm/ge25519_nielsadd2.s5791
-rw-r--r--ext/ed25519-amd64-asm/ge25519_nielsadd_p1p1.s3072
-rw-r--r--ext/ed25519-amd64-asm/ge25519_p1p1_to_p2.s2236
-rw-r--r--ext/ed25519-amd64-asm/ge25519_p1p1_to_p3.s2926
-rw-r--r--ext/ed25519-amd64-asm/ge25519_pack.c13
-rw-r--r--ext/ed25519-amd64-asm/ge25519_pnielsadd_p1p1.s3662
-rw-r--r--ext/ed25519-amd64-asm/ge25519_scalarmult_base.c68
-rw-r--r--ext/ed25519-amd64-asm/ge25519_unpackneg.c60
-rw-r--r--ext/ed25519-amd64-asm/heap_rootreplaced.s476
-rw-r--r--ext/ed25519-amd64-asm/heap_rootreplaced_1limb.s416
-rw-r--r--ext/ed25519-amd64-asm/heap_rootreplaced_2limbs.s436
-rw-r--r--ext/ed25519-amd64-asm/heap_rootreplaced_3limbs.s456
-rw-r--r--ext/ed25519-amd64-asm/hram.c16
-rw-r--r--ext/ed25519-amd64-asm/hram.h8
-rw-r--r--ext/ed25519-amd64-asm/implementors5
-rw-r--r--ext/ed25519-amd64-asm/index_heap.c58
-rw-r--r--ext/ed25519-amd64-asm/index_heap.h31
-rw-r--r--ext/ed25519-amd64-asm/keypair.c25
-rw-r--r--ext/ed25519-amd64-asm/open.c49
-rw-r--r--ext/ed25519-amd64-asm/sc25519.h66
-rw-r--r--ext/ed25519-amd64-asm/sc25519_add.s232
-rw-r--r--ext/ed25519-amd64-asm/sc25519_barrett.s1188
-rw-r--r--ext/ed25519-amd64-asm/sc25519_from32bytes.c55
-rw-r--r--ext/ed25519-amd64-asm/sc25519_from64bytes.c7
-rw-r--r--ext/ed25519-amd64-asm/sc25519_from_shortsc.c9
-rw-r--r--ext/ed25519-amd64-asm/sc25519_iszero.c10
-rw-r--r--ext/ed25519-amd64-asm/sc25519_lt.s131
-rw-r--r--ext/ed25519-amd64-asm/sc25519_mul.c12
-rw-r--r--ext/ed25519-amd64-asm/sc25519_mul_shortsc.c9
-rw-r--r--ext/ed25519-amd64-asm/sc25519_slide.c49
-rw-r--r--ext/ed25519-amd64-asm/sc25519_sub_nored.s142
-rw-r--r--ext/ed25519-amd64-asm/sc25519_to32bytes.c8
-rw-r--r--ext/ed25519-amd64-asm/sc25519_window4.c27
-rw-r--r--ext/ed25519-amd64-asm/sign.c165
-rw-r--r--ext/ed25519-amd64-asm/ull4_mul.s716
-rw-r--r--ext/installfiles/linux/zerotier-containerized/Dockerfile2
-rwxr-xr-xext/installfiles/mac/ZeroTier One.pkgproj22
-rwxr-xr-xext/installfiles/mac/launch.sh6
-rwxr-xr-xext/installfiles/mac/postinst.sh21
-rwxr-xr-xext/installfiles/mac/preinst.sh13
-rw-r--r--ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x64).aip60
-rw-r--r--ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x86).aip60
-rw-r--r--ext/installfiles/windows/ZeroTier One.aip946
-rw-r--r--ext/installfiles/windows/chocolatey/zerotier-one/tools/chocolateyinstall.ps14
-rw-r--r--ext/installfiles/windows/chocolatey/zerotier-one/zerotier-one.nuspec8
-rw-r--r--ext/json/LICENSE.MIT17
-rw-r--r--ext/json/README.md544
-rw-r--r--ext/json/json.hpp16375
-rw-r--r--ext/librethinkdbxx/.travis.yml11
-rw-r--r--ext/librethinkdbxx/COPYRIGHT16
-rw-r--r--ext/librethinkdbxx/Makefile126
-rw-r--r--ext/librethinkdbxx/README.md72
-rw-r--r--ext/librethinkdbxx/reql/add_docs.py80
-rw-r--r--ext/librethinkdbxx/reql/gen.py33
-rw-r--r--ext/librethinkdbxx/reql/python_docs.txt189
-rw-r--r--ext/librethinkdbxx/reql/ql2.proto843
-rw-r--r--ext/librethinkdbxx/src/connection.cc434
-rw-r--r--ext/librethinkdbxx/src/connection.h59
-rw-r--r--ext/librethinkdbxx/src/connection_p.h133
-rw-r--r--ext/librethinkdbxx/src/cursor.cc223
-rw-r--r--ext/librethinkdbxx/src/cursor.h76
-rw-r--r--ext/librethinkdbxx/src/cursor_p.h29
-rw-r--r--ext/librethinkdbxx/src/datum.cc449
-rw-r--r--ext/librethinkdbxx/src/datum.h287
-rw-r--r--ext/librethinkdbxx/src/error.h46
-rw-r--r--ext/librethinkdbxx/src/exceptions.h13
-rw-r--r--ext/librethinkdbxx/src/json.cc62
-rw-r--r--ext/librethinkdbxx/src/json_p.h19
-rw-r--r--ext/librethinkdbxx/src/rapidjson-config.h8
-rw-r--r--ext/librethinkdbxx/src/rapidjson/allocators.h263
-rw-r--r--ext/librethinkdbxx/src/rapidjson/document.h2575
-rw-r--r--ext/librethinkdbxx/src/rapidjson/encodedstream.h299
-rw-r--r--ext/librethinkdbxx/src/rapidjson/encodings.h716
-rw-r--r--ext/librethinkdbxx/src/rapidjson/error/en.h74
-rw-r--r--ext/librethinkdbxx/src/rapidjson/error/error.h155
-rw-r--r--ext/librethinkdbxx/src/rapidjson/filereadstream.h99
-rw-r--r--ext/librethinkdbxx/src/rapidjson/filewritestream.h104
-rw-r--r--ext/librethinkdbxx/src/rapidjson/fwd.h151
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/biginteger.h290
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/diyfp.h258
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/dtoa.h245
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/ieee754.h78
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/itoa.h304
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/meta.h181
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/pow10.h55
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/regex.h701
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/stack.h230
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/strfunc.h55
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/strtod.h269
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/swap.h46
-rw-r--r--ext/librethinkdbxx/src/rapidjson/istreamwrapper.h115
-rw-r--r--ext/librethinkdbxx/src/rapidjson/memorybuffer.h70
-rw-r--r--ext/librethinkdbxx/src/rapidjson/memorystream.h71
-rw-r--r--ext/librethinkdbxx/src/rapidjson/msinttypes/inttypes.h316
-rw-r--r--ext/librethinkdbxx/src/rapidjson/msinttypes/stdint.h300
-rw-r--r--ext/librethinkdbxx/src/rapidjson/ostreamwrapper.h81
-rw-r--r--ext/librethinkdbxx/src/rapidjson/pointer.h1358
-rw-r--r--ext/librethinkdbxx/src/rapidjson/prettywriter.h249
-rw-r--r--ext/librethinkdbxx/src/rapidjson/rapidjson.h615
-rw-r--r--ext/librethinkdbxx/src/rapidjson/reader.h1879
-rw-r--r--ext/librethinkdbxx/src/rapidjson/schema.h2006
-rw-r--r--ext/librethinkdbxx/src/rapidjson/stream.h179
-rw-r--r--ext/librethinkdbxx/src/rapidjson/stringbuffer.h117
-rw-r--r--ext/librethinkdbxx/src/rapidjson/writer.h609
-rw-r--r--ext/librethinkdbxx/src/term.cc285
-rw-r--r--ext/librethinkdbxx/src/term.h592
-rw-r--r--ext/librethinkdbxx/src/types.cc47
-rw-r--r--ext/librethinkdbxx/src/types.h53
-rw-r--r--ext/librethinkdbxx/src/utils.cc153
-rw-r--r--ext/librethinkdbxx/src/utils.h19
-rw-r--r--ext/miniupnpc/Changelog.txt16
-rw-r--r--ext/miniupnpc/LICENSE2
-rw-r--r--ext/miniupnpc/MANIFEST.in5
-rw-r--r--ext/miniupnpc/README7
-rw-r--r--ext/miniupnpc/apiversions.txt172
-rw-r--r--ext/miniupnpc/connecthostport.c8
-rwxr-xr-xext/miniupnpc/external-ip.sh4
-rw-r--r--ext/miniupnpc/minihttptestserver.c659
-rw-r--r--ext/miniupnpc/minisoap.c10
-rw-r--r--ext/miniupnpc/minissdpc.c42
-rw-r--r--ext/miniupnpc/miniupnpc.h2
-rw-r--r--ext/miniupnpc/miniupnpcmodule.c16
-rw-r--r--ext/miniupnpc/miniwget.c25
-rw-r--r--ext/miniupnpc/minixml.c10
-rw-r--r--ext/miniupnpc/pymoduletest.py88
-rw-r--r--ext/miniupnpc/receivedata.c4
-rw-r--r--ext/miniupnpc/testdesc/linksys_WAG200G_desc.values14
-rw-r--r--ext/miniupnpc/testdesc/linksys_WAG200G_desc.xml110
-rw-r--r--ext/miniupnpc/testdesc/new_LiveBox_desc.values20
-rw-r--r--ext/miniupnpc/testdesc/new_LiveBox_desc.xml90
-rw-r--r--ext/miniupnpc/testigddescparse.c187
-rw-r--r--ext/miniupnpc/testminiwget.c55
-rwxr-xr-xext/miniupnpc/testminiwget.sh96
-rw-r--r--ext/miniupnpc/testminixml.c89
-rw-r--r--ext/miniupnpc/testportlistingparse.c151
-rw-r--r--ext/miniupnpc/testreplyparse/DeletePortMapping.namevalue3
-rw-r--r--ext/miniupnpc/testreplyparse/DeletePortMapping.xml6
-rw-r--r--ext/miniupnpc/testreplyparse/GetExternalIPAddress.namevalue2
-rw-r--r--ext/miniupnpc/testreplyparse/GetExternalIPAddress.xml2
-rw-r--r--ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.namevalue3
-rw-r--r--ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.xml3
-rw-r--r--ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.namevalue5
-rw-r--r--ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.xml2
-rw-r--r--ext/miniupnpc/testreplyparse/SetDefaultConnectionService.namevalue1
-rw-r--r--ext/miniupnpc/testreplyparse/SetDefaultConnectionService.xml1
-rw-r--r--ext/miniupnpc/testreplyparse/readme.txt7
-rwxr-xr-xext/miniupnpc/testupnpigd.py84
-rw-r--r--ext/miniupnpc/testupnpreplyparse.c96
-rwxr-xr-xext/miniupnpc/testupnpreplyparse.sh14
-rw-r--r--ext/miniupnpc/upnpc.c12
-rw-r--r--ext/miniupnpc/upnpcommands.c91
-rw-r--r--ext/miniupnpc/upnpreplyparse.c15
-rw-r--r--ext/misc/linux-old-glibc-compat.c18
-rw-r--r--include/ZeroTierDebug.h114
-rw-r--r--include/ZeroTierOne.h939
-rw-r--r--java/jni/Android.mk35
-rw-r--r--java/jni/Application.mk2
-rw-r--r--java/jni/ZT_jniutils.cpp3
-rw-r--r--java/jni/ZT_jniutils.h32
-rw-r--r--java/jni/com_zerotierone_sdk_Node.cpp518
-rw-r--r--java/jni/com_zerotierone_sdk_Node.h2
-rw-r--r--java/src/com/zerotier/sdk/DataStoreGetListener.java8
-rw-r--r--java/src/com/zerotier/sdk/Node.java6
-rw-r--r--java/src/com/zerotier/sdk/PacketSender.java4
-rw-r--r--java/src/com/zerotier/sdk/PathChecker.java4
-rw-r--r--macui/ZeroTier One/about.html119
-rw-r--r--make-bsd.mk79
-rw-r--r--make-linux.mk226
-rw-r--r--make-mac.mk52
-rw-r--r--node/Address.hpp25
-rw-r--r--node/Array.hpp107
-rw-r--r--node/AtomicCounter.hpp26
-rw-r--r--node/Buffer.hpp153
-rw-r--r--node/C25519.cpp2416
-rw-r--r--node/C25519.hpp73
-rw-r--r--node/Capability.cpp13
-rw-r--r--node/Capability.hpp49
-rw-r--r--node/CertificateOfMembership.cpp24
-rw-r--r--node/CertificateOfMembership.hpp34
-rw-r--r--node/CertificateOfOwnership.cpp13
-rw-r--r--node/CertificateOfOwnership.hpp30
-rw-r--r--node/CertificateOfRepresentation.hpp180
-rw-r--r--node/Constants.hpp112
-rw-r--r--node/Credential.hpp11
-rw-r--r--node/Dictionary.hpp100
-rw-r--r--node/Hashtable.hpp52
-rw-r--r--node/Identity.cpp77
-rw-r--r--node/Identity.hpp75
-rw-r--r--node/IncomingPacket.cpp1949
-rw-r--r--node/IncomingPacket.hpp19
-rw-r--r--node/InetAddress.cpp239
-rw-r--r--node/InetAddress.hpp173
-rw-r--r--node/MAC.hpp118
-rw-r--r--node/Membership.cpp50
-rw-r--r--node/Membership.hpp33
-rw-r--r--node/MulticastGroup.hpp47
-rw-r--r--node/Multicaster.cpp157
-rw-r--r--node/Multicaster.hpp102
-rw-r--r--node/Mutex.hpp128
-rw-r--r--node/Network.cpp616
-rw-r--r--node/Network.hpp37
-rw-r--r--node/NetworkConfig.cpp39
-rw-r--r--node/NetworkConfig.hpp216
-rw-r--r--node/NetworkController.hpp10
-rw-r--r--node/Node.cpp643
-rw-r--r--node/Node.hpp128
-rw-r--r--node/OutboundMulticast.cpp25
-rw-r--r--node/OutboundMulticast.hpp26
-rw-r--r--node/Packet.cpp276
-rw-r--r--node/Packet.hpp199
-rw-r--r--node/Path.cpp14
-rw-r--r--node/Path.hpp176
-rw-r--r--node/Peer.cpp536
-rw-r--r--node/Peer.hpp313
-rw-r--r--node/Poly1305.cpp3
-rw-r--r--node/Poly1305.hpp13
-rw-r--r--node/Revocation.cpp13
-rw-r--r--node/Revocation.hpp20
-rw-r--r--node/RuntimeEnvironment.hpp51
-rw-r--r--node/SHA512.cpp593
-rw-r--r--node/SHA512.hpp10
-rw-r--r--node/Salsa20.hpp41
-rw-r--r--node/SelfAwareness.cpp73
-rw-r--r--node/SelfAwareness.hpp22
-rw-r--r--node/SharedPtr.hpp92
-rw-r--r--node/Switch.cpp457
-rw-r--r--node/Switch.hpp80
-rw-r--r--node/Tag.cpp13
-rw-r--r--node/Tag.hpp22
-rw-r--r--node/Topology.cpp231
-rw-r--r--node/Topology.hpp191
-rw-r--r--node/Trace.cpp540
-rw-r--r--node/Trace.hpp176
-rw-r--r--node/Utils.cpp131
-rw-r--r--node/Utils.hpp387
-rw-r--r--node/World.hpp30
-rw-r--r--objects.mk19
-rw-r--r--one.cpp146
-rw-r--r--osdep/Arp.cpp10
-rw-r--r--osdep/Arp.hpp10
-rw-r--r--osdep/BSDEthernetTap.cpp88
-rw-r--r--osdep/BSDEthernetTap.hpp11
-rw-r--r--osdep/Binder.hpp544
-rw-r--r--osdep/BlockingQueue.hpp57
-rw-r--r--osdep/Http.cpp45
-rw-r--r--osdep/Http.hpp10
-rw-r--r--osdep/LinuxEthernetTap.cpp109
-rw-r--r--osdep/LinuxEthernetTap.hpp11
-rw-r--r--osdep/ManagedRoute.cpp30
-rw-r--r--osdep/ManagedRoute.hpp32
-rw-r--r--osdep/NeighborDiscovery.cpp14
-rw-r--r--osdep/NeighborDiscovery.hpp10
-rw-r--r--osdep/OSUtils.cpp81
-rw-r--r--osdep/OSUtils.hpp81
-rw-r--r--osdep/OSXEthernetTap.cpp52
-rw-r--r--osdep/OSXEthernetTap.hpp11
-rw-r--r--osdep/Phy.hpp39
-rw-r--r--osdep/PortMapper.cpp64
-rw-r--r--osdep/PortMapper.hpp10
-rw-r--r--osdep/TestEthernetTap.hpp161
-rw-r--r--osdep/Thread.hpp54
-rw-r--r--osdep/WindowsEthernetTap.cpp137
-rw-r--r--osdep/WindowsEthernetTap.hpp23
-rw-r--r--root-watcher/README.md8
-rw-r--r--root-watcher/config.json.example30
-rw-r--r--root-watcher/package.json16
-rw-r--r--root-watcher/schema.sql21
-rw-r--r--root-watcher/zerotier-root-watcher.js235
-rw-r--r--selftest.cpp202
-rw-r--r--service/OneService.cpp1337
-rw-r--r--service/OneService.hpp26
-rw-r--r--service/README.md11
-rw-r--r--service/SoftwareUpdater.cpp85
-rw-r--r--service/SoftwareUpdater.hpp18
-rw-r--r--tcp-proxy/Makefile7
-rw-r--r--tcp-proxy/README.md4
-rw-r--r--tcp-proxy/tcp-proxy.cpp317
-rw-r--r--version.h12
-rw-r--r--windows/WinUI/APIHandler.cs197
-rw-r--r--windows/WinUI/AboutView.xaml32
-rw-r--r--windows/WinUI/CentralAPI.cs245
-rw-r--r--windows/WinUI/CentralLogin.cs30
-rw-r--r--windows/WinUI/CentralNetwork.cs48
-rw-r--r--windows/WinUI/CentralServer.cs23
-rw-r--r--windows/WinUI/CentralToken.cs21
-rw-r--r--windows/WinUI/CentralUser.cs51
-rw-r--r--windows/WinUI/ISwitchable.cs13
-rw-r--r--windows/WinUI/JoinNetworkView.xaml.cs2
-rw-r--r--windows/WinUI/NetworkInfoView.xaml.cs12
-rw-r--r--windows/WinUI/NetworkMonitor.cs5
-rw-r--r--windows/WinUI/NetworkNameGenerator.cs201
-rw-r--r--windows/WinUI/OnboardProcess/CreateAccount.xaml38
-rw-r--r--windows/WinUI/OnboardProcess/CreateAccount.xaml.cs66
-rw-r--r--windows/WinUI/OnboardProcess/CreateOrJoin.xaml49
-rw-r--r--windows/WinUI/OnboardProcess/CreateOrJoin.xaml.cs98
-rw-r--r--windows/WinUI/OnboardProcess/EnterToken.xaml29
-rw-r--r--windows/WinUI/OnboardProcess/EnterToken.xaml.cs57
-rw-r--r--windows/WinUI/OnboardProcess/Finished.xaml27
-rw-r--r--windows/WinUI/OnboardProcess/Finished.xaml.cs37
-rw-r--r--windows/WinUI/OnboardProcess/LogIn.xaml35
-rw-r--r--windows/WinUI/OnboardProcess/LogIn.xaml.cs57
-rw-r--r--windows/WinUI/OnboardProcess/RegisterOrLogIn.xaml27
-rw-r--r--windows/WinUI/OnboardProcess/RegisterOrLogIn.xaml.cs48
-rw-r--r--windows/WinUI/PageSwitcher.xaml13
-rw-r--r--windows/WinUI/PageSwitcher.xaml.cs56
-rw-r--r--windows/WinUI/PreferencesView.xaml23
-rw-r--r--windows/WinUI/PreferencesView.xaml.cs42
-rw-r--r--windows/WinUI/Switcher.cs24
-rw-r--r--windows/WinUI/Themes/Generic.xaml1
-rw-r--r--windows/WinUI/ToolbarItem.xaml3
-rw-r--r--windows/WinUI/ToolbarItem.xaml.cs64
-rw-r--r--windows/WinUI/WinUI.csproj59
-rw-r--r--windows/ZeroTierOne/ZeroTierOne.vcxproj818
-rw-r--r--windows/ZeroTierOne/ZeroTierOne.vcxproj.filters1016
-rw-r--r--windows/ZeroTierOneSDK.sln28
-rw-r--r--windows/ZeroTierOneSDK/ZeroTierOneSDK.vcxproj257
-rw-r--r--windows/ZeroTierOneSDK/ZeroTierOneSDK.vcxproj.filters240
-rw-r--r--windows/ZeroTierOneSDK/dllmain.cpp22
-rw-r--r--windows/ZeroTierOneSDK/targetver.h8
-rw-r--r--zerotier-one.spec2
482 files changed, 90319 insertions, 24286 deletions
diff --git a/.gitignore b/.gitignore
index bd884dc8..49733d1f 100755
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,3 @@
-<<<<<<< HEAD
# Main binaries created in *nix builds
/zerotier-one
/zerotier-idtool
@@ -11,6 +10,7 @@
.Apple*
Thumbs.db
@eaDir
+._*
# Windows build droppings
/windows/ZeroTierOne.sdf
@@ -27,6 +27,7 @@ Thumbs.db
/windows/Release
/windows/WebUIWrapper/bin
/windows/WebUIWrapper/obj
+/windows/lib
/ext/installfiles/windows/ZeroTier One-SetupFiles
/ext/installfiles/windows/Prerequisites
/ext/installfiles/windows/*-cache
@@ -111,3 +112,7 @@ build/
!default.perspectivev3
*.xccheckout
xcuserdata/
+ext/librethinkdbxx/build
+.vscode
+__pycache__
+*~
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 00000000..fff7808e
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,12 @@
+# CMake build script for libzerotiercore.a
+
+cmake_minimum_required (VERSION 2.8)
+project (zerotiercore)
+
+set (PROJ_DIR ${PROJECT_SOURCE_DIR})
+set (ZT_DEFS -std=c++11)
+
+file(GLOB core_src_glob ${PROJ_DIR}/node/*.cpp)
+add_library(zerotiercore STATIC ${core_src_glob})
+
+target_compile_options(zerotiercore PRIVATE ${ZT_DEFS})
diff --git a/COPYING b/COPYING
index 23d42dfa..8008d3e0 100644
--- a/COPYING
+++ b/COPYING
@@ -1,5 +1,5 @@
ZeroTier One, an endpoint server for the ZeroTier virtual network layer.
-Copyright © 2011–2016 ZeroTier, Inc.
+Copyright © 2011–2018 ZeroTier, Inc.
ZeroTier One is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/Jenkinsfile b/Jenkinsfile
index 74c86249..e729c334 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -1,9 +1,11 @@
#!/usr/bin/env groovy
node('master') {
+ checkout scm
+
def changelog = getChangeLog currentBuild
- slackSend "Building ${env.JOB_NAME} #${env.BUILD_NUMBER} \n Change Log: \n ${changelog}"
+ mattermostSend "Building ${env.JOB_NAME} #${env.BUILD_NUMBER} \n Change Log: \n ${changelog}"
}
parallel 'centos7': {
@@ -17,7 +19,7 @@ parallel 'centos7': {
}
catch (err) {
currentBuild.result = "FAILURE"
- slackSend color: '#ff0000', message: "${env.JOB_NAME} broken on Centos 7 (<${env.BUILD_URL}|Open>)"
+ mattermostSend color: '#ff0000', message: "${env.JOB_NAME} broken on Centos 7 (<${env.BUILD_URL}|Open>)"
throw err
}
@@ -28,12 +30,12 @@ parallel 'centos7': {
checkout scm
stage('Build Android NDK') {
- sh "/android/android-ndk-r13b/ndk-build -C $WORKSPACE/java ZT1=${WORKSPACE}"
+ sh "/android/android-ndk-r15b/ndk-build -C $WORKSPACE/java ZT1=${WORKSPACE}"
}
}
catch (err) {
currentBuild.result = "FAILURE"
- slackSend color: '#ff0000', message: "${env.JOB_NAME} broken on Android NDK (<${env.BUILD_URL}|Open>)"
+ mattermostSend color: '#ff0000', message: "${env.JOB_NAME} broken on Android NDK (<${env.BUILD_URL}|Open>)"
throw err
}
@@ -53,7 +55,7 @@ parallel 'centos7': {
}
catch (err) {
currentBuild.result = "FAILURE"
- slackSend color: '#ff0000', message: "${env.JOB_NAME} broken on macOS (<${env.BUILD_URL}|Open>)"
+ mattermostSend color: '#ff0000', message: "${env.JOB_NAME} broken on macOS (<${env.BUILD_URL}|Open>)"
throw err
}
@@ -72,11 +74,11 @@ msbuild windows\\ZeroTierOne.sln
}
catch (err) {
currentBuild.result = "FAILURE"
- slackSend color: '#ff0000', message: "${env.JOB_NAME} broken on Windows (<${env.BUILD_URL}|Open>)"
+ mattermostSend color: '#ff0000', message: "${env.JOB_NAME} broken on Windows (<${env.BUILD_URL}|Open>)"
throw err
}
}
}
-slackSend color: "#00ff00", message: "${env.JOB_NAME} #${env.BUILD_NUMBER} Complete (<${env.BUILD_URL}|Show More...>)"
+mattermostSend color: "#00ff00", message: "${env.JOB_NAME} #${env.BUILD_NUMBER} Complete (<${env.BUILD_URL}|Show More...>)"
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 00000000..8fd274be
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,37 @@
+ZeroTier One - Network Virtualization Everywhere
+Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+See LICENSE.GPL-3 for the full GNU GPL v3 license.
+
+--
+
+You can be released from the requirements of the license by purchasing
+a commercial license. Buying such a license is mandatory as soon as you
+develop commercial closed-source software that incorporates or links
+directly against ZeroTier software without disclosing the source code
+of your own application.
+
+--
+
+The above license does not apply to third party code included with or
+linked against by ZeroTier software. See the third party code section
+of the AUTHORS.md for an index of third party software included in
+this software repository.
+
+Licenses for third party code are all relatively permissive: MIT,
+BSD, and public domain. The only exception is the tap-windows driver
+which is under the GPLv2, but this is only needed to produce the
+binary tap device driver used by the ZeroTier service on Windows.
diff --git a/OFFICIAL-RELEASE-STEPS.md b/OFFICIAL-RELEASE-STEPS.md
index d0f42e35..4db5169f 100644
--- a/OFFICIAL-RELEASE-STEPS.md
+++ b/OFFICIAL-RELEASE-STEPS.md
@@ -31,22 +31,7 @@ You will need [Packages](http://s.sudre.free.fr/Software/Packages/about.html) an
## Linux
-Mount the GPG key for *contact@zerotier.com* and then on an x86_64 box with a recent version of Docker and an Internet connection run:
-
- make distclean
- cd linux-build-farm
- ./build.sh
-
-This will build i386 and x86_64 packages. Now ssh into our build Raspberry Pi and type `make debian` there to build the Raspbian armhf package. Copy it to `debian-jessie/` inside `linux-build-farm` so that it will be included in the repositories we generate. Now generate the YUM and APT repos:
-
- rm -rf ~/.aptly*
- rm -rf /tmp/zt-rpm-repo
- ./make-apt-repos.sh
- ./make-rpm-repos.sh
-
-This will require the passphrase for *contact@zerotier.com*.
-
-The contents of ~/.aptly/public must be published as `debian/` on `download.zerotier.com`. The contents of /tmp/zt-rpm-repo are published as `redhat/` on same.
+See `LinuxBuild` environment on `linux-build` VM and use: `chroots/mount-build.sh`, `chroots/build.sh`, and the scripts in `build/` to make APT and RPM repositories.
## Windows
diff --git a/README.md b/README.md
index d4163b7f..37f04982 100644
--- a/README.md
+++ b/README.md
@@ -1,29 +1,19 @@
ZeroTier - A Planetary Ethernet Switch
======
-ZeroTier is an enterprise Ethernet switch for planet Earth.
+ZeroTier is a smart programmable Ethernet switch for planet Earth.
-It erases the LAN/WAN distinction and makes VPNs, tunnels, proxies, and other kludges arising from the inflexible nature of physical networks obsolete. Everything is encrypted end-to-end and traffic takes the most direct (peer to peer) path available.
+It replaces the physical LAN/WAN boundary with a virtual one, allowing devices of any type at any location to be managed as if they all reside in the same cloud region or data center. All traffic is encrypted end-to-end and takes the most direct path available for minimum latency and maximum performance. The goals and design of ZeroTier are inspired by among other things the original [Google BeyondCorp](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/43231.pdf) paper and the [Jericho Forum](https://en.wikipedia.org/wiki/Jericho_Forum).
-Visit [ZeroTier's site](https://www.zerotier.com/) for more information and [pre-built binary packages](https://www.zerotier.com/download.shtml). Apps for Android and iOS are available for free in the Google Play and Apple app stores.
+Visit [ZeroTier's site](https://www.zerotier.com/?pk_campaign=github_ZeroTierOne) for more information and [pre-built binary packages](https://www.zerotier.com/download.shtml?pk_campaign=github_ZeroTierOne). Apps for Android and iOS are available for free in the Google Play and Apple app stores.
### Getting Started
-ZeroTier's basic operation is easy to understand. Devices have 10-digit *ZeroTier addresses* like `89e92ceee5` and networks have 16-digit network IDs like `8056c2e21c000001`. All it takes for a device to join a network is its 16-digit ID, and all it takes for a network to authorize a device is its 10-digit address. Everything else is automatic.
+Everything in the ZeroTier world is controlled by two types of identifier: 40-bit/10-digit *ZeroTier addresses* and 64-bit/16-digit *network IDs*. A ZeroTier address identifies a node or "device" (laptop, phone, server, VM, app, etc.) while a network ID identifies a virtual Ethernet network that can be joined by devices.
-A "device" in our terminology is any "unit of compute" capable of talking to a network: desktops, laptops, phones, servers, VMs/VPSes, containers, and even user-space applications via our [SDK](https://github.com/zerotier/ZeroTierSDK).
+Another way of thinking about it is that ZeroTier addresses are port numbers on a giant planetary-sized smart switch while network IDs are VLANs to which these ports can be assigned. For more details read about VL1 and VL2 in [the ZeroTier manual](https://www.zerotier.com/manual.shtml).
-For testing purposes we provide a public virtual network called *Earth* with network ID `8056c2e21c000001`. You can join it with:
-
- sudo zerotier-cli join 8056c2e21c000001
-
-Now wait about 30 seconds and check your system with `ip addr list` or `ifconfig`. You'll see a new interface whose name starts with *zt* and it should quickly get an IPv4 and an IPv6 address. Once you see it get an IP, try pinging `earth.zerotier.net`. If you've joined Earth from more than one system, try pinging your other machine. If you don't want to belong to a giant Ethernet party line anymore, just type:
-
- sudo zerotier-cli leave 8056c2e21c000001
-
-The *zt* interface will disappear. You're no longer on the network.
-
-To create networks of your own, you'll need a network controller. ZeroTier One (for desktops and servers) includes controller functionality in its default build that can be configured via its JSON API (see [README.md in controller/](controller/)). ZeroTier provides a hosted solution with a nice web UI and SaaS add-ons at [my.zerotier.com](https://my.zerotier.com/). Basic controller functionality is free for up to 100 devices.
+*Network controllers* are ZeroTier nodes that act as access control certificate authorities and configuration managers for virtual networks. The first 40 bits (or 10 digits) of a network ID is the ZeroTier address of its controller. You can create networks with our [hosted controllers](https://my.zerotier.com/) and web UI/API or [host your own](controller/) if you don't mind posting some JSON configuration info or writing a script to do so.
### Project Layout
@@ -56,10 +46,9 @@ To build on Mac and Linux just type `make`. On FreeBSD and OpenBSD `gmake` (GNU
- Linux makefiles automatically detect and prefer clang/clang++ if present as it produces smaller and slightly faster binaries in most cases. You can override by supplying CC and CXX variables on the make command line.
- CentOS 7 ships with a version of GCC/G++ that is too old, but a new enough version of CLANG can be found in the *epel* repositories. Type `yum install epel-release` and then `yum install clang` to build there.
- **Windows**
- - Windows 7 or newer (and equivalent server versions) are supported. This *may* work on Vista but you're on your own there. Windows XP is not supported since it lacks many important network API functions.
- - We build with Visual Studio 2015. Older versions may not work with the solution file and project files we ship and may not have new enough C++11 support.
- - Pre-built signed Windows drivers are included in `ext/bin/tap-windows-ndis6`. The MSI files found there will install them on 32-bit and 64-bit systems. (These are included in our multi-architecture installer as chained MSIs.)
- - Windows builds are more painful in general than other platforms and are for the adventurous.
+ - Windows 7 or newer is supported. This *may* work on Vista but isn't officially supported there. It will not work on Windows XP.
+ - We build with Visual Studio 2015. Older versions may not work. Clang or MinGW will also probably work but may require some makefile hacking.
+ - Pre-built signed Windows drivers are included in `ext/bin/tap-windows-ndis6`. The MSI files found there will install them on 32-bit and 64-bit systems. We don't recommend trying to build Windows drivers from scratch unless you know what you're doing. One does not simply "build" a Windows driver.
- **FreeBSD**
- Tested most recently on FreeBSD-11. Older versions may work but we're not sure.
- GCC/G++ 4.9 and gmake are required. These can be installed from packages or ports. Type `gmake` to build.
@@ -112,7 +101,7 @@ ZeroTier One peers will automatically locate each other and communicate directly
Users behind certain types of firewalls and "symmetric" NAT devices may not able able to connect to external peers directly at all. ZeroTier has limited support for port prediction and will *attempt* to traverse symmetric NATs, but this doesn't always work. If P2P connectivity fails you'll be bouncing UDP packets off our relay servers resulting in slower performance. Some NAT router(s) have a configurable NAT mode, and setting this to "full cone" will eliminate this problem. If you do this you may also see a magical improvement for things like VoIP phones, Skype, BitTorrent, WebRTC, certain games, etc., since all of these use NAT traversal techniques similar to ours.
-If you're interested, there's a [technical deep dive about NAT traversal on our blog](https://www.zerotier.com/blog/?p=226). A troubleshooting tool to help you diagnose NAT issues is planned for the future as are uPnP/IGD/NAT-PMP and IPv6 transport.
+If you're interested, there's a [technical deep dive about NAT traversal on our blog](https://www.zerotier.com/blog/?p=226?pk_campaign=github_ZeroTierOne). A troubleshooting tool to help you diagnose NAT issues is planned for the future as are uPnP/IGD/NAT-PMP and IPv6 transport.
If a firewall between you and the Internet blocks ZeroTier's UDP traffic, you will fall back to last-resort TCP tunneling to rootservers over port 443 (https impersonation). This will work almost anywhere but is *very slow* compared to UDP or direct peer to peer connectivity.
diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index 195e8888..a73671d7 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -1,6 +1,41 @@
ZeroTier Release Notes
======
+# 2018-04-17 -- Version 1.2.6
+
+ * Features and Core Improvements
+ * Path selection has been overhauled to improve path stability, simplify code, and prepare for multi-path and trunking in the next major release.
+ * This version introduces remote tracing for remote diagnostics. Network controllers can set a node (usually the controller itself) to receive remote tracing events from all members of the network or from select members. Events are only sent if they pertain to a given network for security reasons.
+ * Multicast replication can now be done by designated multicast replicators on a network (flagged as such at the controller) rather than by the sender. Most users won't want this, but it's useful for specialized use cases on hub-and-spoke networks and for low-power devices.
+ * Cryptographic performance improvements on several platforms.
+ * Multithreaded performance improvements throughout the code base, including the use of an inline lightweight spinlock for low-contention resources.
+ * Bugs fixed
+ * Disappearing routes on Mac (GitHub issue #600)
+ * Route flapping and path instability in some dual-stack V4/V6 networks
+ * Blacklist (in local.conf) doesn't work reliably (GitHub issue #656)
+ * Connection instabilities due to unsigned integer overflows in timing comparisons (use int64_t instead of uint64_t)
+ * Binaries don't run on some older or lower-end 32-bit ARM chips (build problem)
+ * ARM NEON crypto code crashes (build problem)
+ * Fixed some lock ordering issues revealed by "valgrind" tool
+ * The "zerotier-idtool" command could not be accessed from "zerotier-one" via command line switch
+ * Leaking sockets on some platforms when uPnP/NAT-PMP is enabled
+ * Fixed two very rare multithreading issues that were only observed on certain systems
+ * Platform-Specific Changes
+ * MacOS
+ * Installer now loads the kernel extension right away so that High Sierra users will see the prompt to authorize it. This is done in the "Security & Privacy" preference pane and must be done driectly on the console (not via remote desktop). On High Sierra and newer kexts must be authorized at the console via security settings system preferences pane.
+ * Windows
+ * The Windows installer should now install the driver without requiring a special prompt in most cases. This should make it easier for our packages to be accepted into and updated in the Chocolatey repository and should make it easier to perform remote installs across groups of machines using IT management and provisioning tools.
+ * The Windows official packages are now signed with an EV certificate (with hardware key).
+ * The Windows UI can now log into ZeroTier Central and join networks via the Central API.
+ * The `zerotier-idtool` command should now work on Windows without ugly hacks.
+ * Upgraded the installer version.
+ * Made a few changes to hopefully fix sporadic "will not uninstall" problems, though we cannot duplicate these issues ourselves.
+ * Linux
+ * Device names are now generated deterministically based on network IDs for all newly joined networks.
+ * Android
+ * Multicast now works on Android in most cases! Android apps can send and receive multicast and subscribe to multicast group IPs. Note that in some cases the app must bind to the specific correct interface for this to work.
+ * IPv6 can be disabled in UI for cases where it causes problems.
+
# 2017-04-20 -- Version 1.2.4
* Managed routes are now only bifurcated for the default route. This is a change in behavior, though few people will probably notice. Bifurcating all managed routes was causing more trouble than it was worth for most users.
diff --git a/artwork/ZeroTierIcon32x32.png b/artwork/ZeroTierIcon32x32.png
new file mode 100644
index 00000000..24ff0a1c
--- /dev/null
+++ b/artwork/ZeroTierIcon32x32.png
Binary files differ
diff --git a/node/Cluster.cpp b/attic/Cluster.cpp
index 54206f99..119aec29 100644
--- a/node/Cluster.cpp
+++ b/attic/Cluster.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifdef ZT_ENABLE_CLUSTER
@@ -249,7 +257,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
memcpy(keytmp,_key,32);
for(int i=0;i<8;++i)
keytmp[i] ^= reinterpret_cast<const char *>(msg)[i];
- Salsa20 s20(keytmp,256,reinterpret_cast<const char *>(msg) + 8);
+ Salsa20 s20(keytmp,reinterpret_cast<const char *>(msg) + 8);
Utils::burn(keytmp,sizeof(keytmp));
// One-time-use Poly1305 key from first 32 bytes of Salsa20 keystream (as per DJB/NaCl "standard")
@@ -948,7 +956,7 @@ void Cluster::_flush(uint16_t memberId)
memcpy(keytmp,m.key,32);
for(int i=0;i<8;++i)
keytmp[i] ^= m.q[i];
- Salsa20 s20(keytmp,256,m.q.field(8,8));
+ Salsa20 s20(keytmp,m.q.field(8,8));
Utils::burn(keytmp,sizeof(keytmp));
// One-time-use Poly1305 key from first 32 bytes of Salsa20 keystream (as per DJB/NaCl "standard")
diff --git a/node/Cluster.hpp b/attic/Cluster.hpp
index 08e32a99..74b091f5 100644
--- a/node/Cluster.hpp
+++ b/attic/Cluster.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_CLUSTER_HPP
diff --git a/service/ClusterDefinition.hpp b/attic/ClusterDefinition.hpp
index dda1a8c8..b6317ff7 100644
--- a/service/ClusterDefinition.hpp
+++ b/attic/ClusterDefinition.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_CLUSTERDEFINITION_HPP
@@ -64,7 +72,7 @@ public:
return;
char myAddressStr[64];
- Utils::snprintf(myAddressStr,sizeof(myAddressStr),"%.10llx",myAddress);
+ Utils::ztsnprintf(myAddressStr,sizeof(myAddressStr),"%.10llx",myAddress);
std::vector<std::string> lines(OSUtils::split(cf.c_str(),"\r\n","",""));
for(std::vector<std::string>::iterator l(lines.begin());l!=lines.end();++l) {
diff --git a/service/ClusterGeoIpService.cpp b/attic/ClusterGeoIpService.cpp
index 89015c51..2dcc9179 100644
--- a/service/ClusterGeoIpService.cpp
+++ b/attic/ClusterGeoIpService.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifdef ZT_ENABLE_CLUSTER
diff --git a/service/ClusterGeoIpService.hpp b/attic/ClusterGeoIpService.hpp
index ff2fcdb8..380f944f 100644
--- a/service/ClusterGeoIpService.hpp
+++ b/attic/ClusterGeoIpService.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_CLUSTERGEOIPSERVICE_HPP
diff --git a/attic/FCV.hpp b/attic/FCV.hpp
new file mode 100644
index 00000000..0fb9e250
--- /dev/null
+++ b/attic/FCV.hpp
@@ -0,0 +1,101 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#include "Constants.hpp"
+
+namespace ZeroTier {
+
+/**
+ * A really simple fixed capacity vector
+ *
+ * This class does no bounds checking, so the user must ensure that
+ * no more than C elements are ever added and that accesses are in
+ * bounds.
+ *
+ * @tparam T Type to contain
+ * @tparam C Capacity of vector
+ */
+template<typename T,unsigned long C>
+class FCV
+{
+public:
+ FCV() : _s(0) {}
+ ~FCV() { clear(); }
+
+ FCV(const FCV &v) :
+ _s(v._s)
+ {
+ for(unsigned long i=0;i<_s;++i) {
+ new (reinterpret_cast<T *>(_mem + (sizeof(T) * i))) T(reinterpret_cast<const T *>(v._mem)[i]);
+ }
+ }
+
+ inline FCV &operator=(const FCV &v)
+ {
+ clear();
+ _s = v._s;
+ for(unsigned long i=0;i<_s;++i) {
+ new (reinterpret_cast<T *>(_mem + (sizeof(T) * i))) T(reinterpret_cast<const T *>(v._mem)[i]);
+ }
+ return *this;
+ }
+
+ typedef T * iterator;
+ typedef const T * const_iterator;
+ typedef unsigned long size_type;
+
+ inline iterator begin() { return (T *)_mem; }
+ inline iterator end() { return (T *)(_mem + (sizeof(T) * _s)); }
+ inline iterator begin() const { return (const T *)_mem; }
+ inline iterator end() const { return (const T *)(_mem + (sizeof(T) * _s)); }
+
+ inline T &operator[](const size_type i) { return reinterpret_cast<T *>(_mem)[i]; }
+ inline const T &operator[](const size_type i) const { return reinterpret_cast<const T *>(_mem)[i]; }
+
+ inline T &front() { return reinterpret_cast<T *>(_mem)[0]; }
+ inline const T &front() const { return reinterpret_cast<const T *>(_mem)[0]; }
+ inline T &back() { return reinterpret_cast<T *>(_mem)[_s - 1]; }
+ inline const T &back() const { return reinterpret_cast<const T *>(_mem)[_s - 1]; }
+
+ inline void push_back(const T &v) { new (reinterpret_cast<T *>(_mem + (sizeof(T) * _s++))) T(v); }
+ inline void pop_back() { reinterpret_cast<T *>(_mem + (sizeof(T) * --_s))->~T(); }
+
+ inline size_type size() const { return _s; }
+ inline size_type capacity() const { return C; }
+
+ inline void clear()
+ {
+ for(unsigned long i=0;i<_s;++i)
+ reinterpret_cast<T *>(_mem + (sizeof(T) * i))->~T();
+ _s = 0;
+ }
+
+private:
+ char _mem[sizeof(T) * C];
+ unsigned long _s;
+};
+
+} // namespace ZeroTier
diff --git a/attic/big-http-test/2015-11-10_01_50000.out.xz b/attic/big-http-test/2015-11-10_01_50000.out.xz
deleted file mode 100644
index d3e2a666..00000000
--- a/attic/big-http-test/2015-11-10_01_50000.out.xz
+++ /dev/null
Binary files differ
diff --git a/attic/big-http-test/2015-11-10_02_50000.out.xz b/attic/big-http-test/2015-11-10_02_50000.out.xz
deleted file mode 100644
index 0154da79..00000000
--- a/attic/big-http-test/2015-11-10_02_50000.out.xz
+++ /dev/null
Binary files differ
diff --git a/attic/big-http-test/2015-11-10_03_12500_ec2-east-only.out.xz b/attic/big-http-test/2015-11-10_03_12500_ec2-east-only.out.xz
deleted file mode 100644
index 3ae3555e..00000000
--- a/attic/big-http-test/2015-11-10_03_12500_ec2-east-only.out.xz
+++ /dev/null
Binary files differ
diff --git a/attic/big-http-test/Dockerfile b/attic/big-http-test/Dockerfile
deleted file mode 100644
index e19b3fee..00000000
--- a/attic/big-http-test/Dockerfile
+++ /dev/null
@@ -1,24 +0,0 @@
-FROM centos:latest
-
-MAINTAINER https://www.zerotier.com/
-
-EXPOSE 9993/udp
-
-ADD nodesource-el.repo /etc/yum.repos.d/nodesource-el.repo
-RUN yum -y update && yum install -y nodejs && yum clean all
-
-RUN mkdir -p /var/lib/zerotier-one
-RUN mkdir -p /var/lib/zerotier-one/networks.d
-RUN touch /var/lib/zerotier-one/networks.d/ffffffffffffffff.conf
-
-ADD package.json /
-RUN npm install
-
-ADD zerotier-one /
-RUN chmod a+x /zerotier-one
-
-ADD agent.js /
-ADD docker-main.sh /
-RUN chmod a+x /docker-main.sh
-
-CMD ["./docker-main.sh"]
diff --git a/attic/big-http-test/README.md b/attic/big-http-test/README.md
deleted file mode 100644
index 23a95605..00000000
--- a/attic/big-http-test/README.md
+++ /dev/null
@@ -1,12 +0,0 @@
-HTTP one-to-all test
-======
-
-*This is really internal use code. You're free to test it out but expect to do some editing/tweaking to make it work. We used this to run some massive scale tests of our new geo-cluster-based root server infrastructure prior to taking it live.*
-
-Before using this code you will want to edit agent.js to change SERVER_HOST to the IP address of where you will run server.js. This should typically be an open Internet IP, since this makes reporting not dependent upon the thing being tested. Also note that this thing does no security of any kind. It's designed for one-off tests run over a short period of time, not to be anything that runs permanently. You will also want to edit the Dockerfile if you want to build containers and change the network ID to the network you want to run tests over.
-
-This code can be deployed across a large number of VMs or containers to test and benchmark HTTP traffic within a virtual network at scale. The agent acts as a server and can query other agents, while the server collects agent data and tells agents about each other. It's designed to use RFC4193-based ZeroTier IPv6 addresses within the cluster, which allows the easy provisioning of a large cluster without IP conflicts.
-
-The Dockerfile builds an image that launches the agent. The image must be "docker run" with "--device=/dev/net/tun --privileged" to permit it to open a tun/tap device within the container. (Unfortunately CAP_NET_ADMIN may not work due to a bug in Docker and/or Linux.) You can run a bunch with a command like:
-
- for ((n=0;n<10;n++)); do docker run --device=/dev/net/tun --privileged -d zerotier/http-test; done
diff --git a/attic/big-http-test/agent.js b/attic/big-http-test/agent.js
deleted file mode 100644
index 9ab2e019..00000000
--- a/attic/big-http-test/agent.js
+++ /dev/null
@@ -1,196 +0,0 @@
-// ZeroTier distributed HTTP test agent
-
-// ---------------------------------------------------------------------------
-// Customizable parameters:
-
-// Time between startup and first test attempt
-var TEST_STARTUP_LAG = 10000;
-
-// Maximum interval between test attempts (actual timing is random % this)
-var TEST_INTERVAL_MAX = (60000 * 10);
-
-// Test timeout in ms
-var TEST_TIMEOUT = 30000;
-
-// Where should I get other agents' IDs and POST results?
-var SERVER_HOST = '52.26.196.147';
-var SERVER_PORT = 18080;
-
-// Which port do agents use to serve up test data to each other?
-var AGENT_PORT = 18888;
-
-// Payload size in bytes
-var PAYLOAD_SIZE = 5000;
-
-// ---------------------------------------------------------------------------
-
-var ipaddr = require('ipaddr.js');
-var os = require('os');
-var http = require('http');
-var async = require('async');
-
-var express = require('express');
-var app = express();
-
-// Find our ZeroTier-assigned RFC4193 IPv6 address
-var thisAgentId = null;
-var interfaces = os.networkInterfaces();
-if (!interfaces) {
- console.error('FATAL: os.networkInterfaces() failed.');
- process.exit(1);
-}
-for(var ifname in interfaces) {
- var ifaddrs = interfaces[ifname];
- if (Array.isArray(ifaddrs)) {
- for(var i=0;i<ifaddrs.length;++i) {
- if (ifaddrs[i].family == 'IPv6') {
- try {
- var ipbytes = ipaddr.parse(ifaddrs[i].address).toByteArray();
- if ((ipbytes.length === 16)&&(ipbytes[0] == 0xfd)&&(ipbytes[9] == 0x99)&&(ipbytes[10] == 0x93)) {
- thisAgentId = '';
- for(var j=0;j<16;++j) {
- var tmp = ipbytes[j].toString(16);
- if (tmp.length === 1)
- thisAgentId += '0';
- thisAgentId += tmp;
- }
- }
- } catch (e) {
- console.error(e);
- }
- }
- }
- }
-}
-if (thisAgentId === null) {
- console.error('FATAL: no ZeroTier-assigned RFC4193 IPv6 addresses found on any local interface!');
- process.exit(1);
-}
-
-//console.log(thisAgentId);
-
-// Create a random (and therefore not very compressable) payload
-var payload = new Buffer(PAYLOAD_SIZE);
-for(var xx=0;xx<PAYLOAD_SIZE;++xx) {
- payload.writeUInt8(Math.round(Math.random() * 255.0),xx);
-}
-
-function agentIdToIp(agentId)
-{
- var ip = '';
- ip += agentId.substr(0,4);
- ip += ':';
- ip += agentId.substr(4,4);
- ip += ':';
- ip += agentId.substr(8,4);
- ip += ':';
- ip += agentId.substr(12,4);
- ip += ':';
- ip += agentId.substr(16,4);
- ip += ':';
- ip += agentId.substr(20,4);
- ip += ':';
- ip += agentId.substr(24,4);
- ip += ':';
- ip += agentId.substr(28,4);
- return ip;
-};
-
-var lastTestResult = null;
-var allOtherAgents = {};
-
-function doTest()
-{
- var submit = http.request({
- host: SERVER_HOST,
- port: SERVER_PORT,
- path: '/'+thisAgentId,
- method: 'POST'
- },function(res) {
- var body = '';
- res.on('data',function(chunk) { body += chunk.toString(); });
- res.on('end',function() {
-
- if (body) {
- try {
- var peers = JSON.parse(body);
- if (Array.isArray(peers)) {
- for(var xx=0;xx<peers.length;++xx)
- allOtherAgents[peers[xx]] = true;
- }
- } catch (e) {}
- }
-
- var agents = Object.keys(allOtherAgents);
- if (agents.length > 1) {
-
- var target = agents[Math.floor(Math.random() * agents.length)];
- while (target === thisAgentId)
- target = agents[Math.floor(Math.random() * agents.length)];
-
- var testRequest = null;
- var timeoutId = null;
- timeoutId = setTimeout(function() {
- if (testRequest !== null)
- testRequest.abort();
- timeoutId = null;
- },TEST_TIMEOUT);
- var startTime = Date.now();
-
- testRequest = http.get({
- host: agentIdToIp(target),
- port: AGENT_PORT,
- path: '/'
- },function(res) {
- var bytes = 0;
- res.on('data',function(chunk) { bytes += chunk.length; });
- res.on('end',function() {
- lastTestResult = {
- source: thisAgentId,
- target: target,
- time: (Date.now() - startTime),
- bytes: bytes,
- timedOut: (timeoutId === null),
- error: null
- };
- if (timeoutId !== null)
- clearTimeout(timeoutId);
- return setTimeout(doTest,Math.round(Math.random() * TEST_INTERVAL_MAX) + 1);
- });
- }).on('error',function(e) {
- lastTestResult = {
- source: thisAgentId,
- target: target,
- time: (Date.now() - startTime),
- bytes: 0,
- timedOut: (timeoutId === null),
- error: e.toString()
- };
- if (timeoutId !== null)
- clearTimeout(timeoutId);
- return setTimeout(doTest,Math.round(Math.random() * TEST_INTERVAL_MAX) + 1);
- });
-
- } else {
- return setTimeout(doTest,1000);
- }
-
- });
- }).on('error',function(e) {
- console.log('POST failed: '+e.toString());
- return setTimeout(doTest,1000);
- });
- if (lastTestResult !== null) {
- submit.write(JSON.stringify(lastTestResult));
- lastTestResult = null;
- }
- submit.end();
-};
-
-// Agents just serve up a test payload
-app.get('/',function(req,res) { return res.status(200).send(payload); });
-
-var expressServer = app.listen(AGENT_PORT,function () {
- // Start timeout-based loop
- setTimeout(doTest(),TEST_STARTUP_LAG);
-});
diff --git a/attic/big-http-test/big-test-kill.sh b/attic/big-http-test/big-test-kill.sh
deleted file mode 100755
index fa7f3cc4..00000000
--- a/attic/big-http-test/big-test-kill.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-
-# Kills all running Docker containers on all big-test-hosts
-
-export PATH=/bin:/usr/bin:/usr/local/bin:/usr/sbin:/sbin
-
-pssh -h big-test-hosts -x '-t -t' -i -OUserKnownHostsFile=/dev/null -OStrictHostKeyChecking=no -t 0 -p 256 "sudo docker ps -aq | xargs -r sudo docker rm -f"
-
-exit 0
diff --git a/attic/big-http-test/big-test-start.sh b/attic/big-http-test/big-test-start.sh
deleted file mode 100755
index 2411eeda..00000000
--- a/attic/big-http-test/big-test-start.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/bash
-
-# More than 500 container seems to result in a lot of sporadic failures, probably due to Linux kernel scaling issues with virtual network ports
-# 250 with a 16GB RAM VM like Amazon m4.xlarge seems good
-NUM_CONTAINERS=250
-CONTAINER_IMAGE=zerotier/http-test
-SCALE_UP_DELAY=10
-
-export PATH=/bin:/usr/bin:/usr/local/bin:/usr/sbin:/sbin
-
-pssh -h big-test-hosts -x '-t -t' -i -OUserKnownHostsFile=/dev/null -OStrictHostKeyChecking=no -t 0 -p 256 "sudo sysctl -w net.netfilter.nf_conntrack_max=262144 ; for ((n=0;n<$NUM_CONTAINERS;n++)); do sudo docker run --device=/dev/net/tun --privileged -d $CONTAINER_IMAGE; sleep $SCALE_UP_DELAY; done"
-
-exit 0
diff --git a/attic/big-http-test/crunch-results.js b/attic/big-http-test/crunch-results.js
deleted file mode 100644
index 50e5c49a..00000000
--- a/attic/big-http-test/crunch-results.js
+++ /dev/null
@@ -1,65 +0,0 @@
-//
-// Pipe the output of server.js into this to convert raw test results into bracketed statistics
-// suitable for graphing.
-//
-
-// Time duration per statistical bracket
-var BRACKET_SIZE = 10000;
-
-// Number of bytes expected from each test
-var EXPECTED_BYTES = 5000;
-
-var readline = require('readline');
-var rl = readline.createInterface({
- input: process.stdin,
- output: process.stdout,
- terminal: false
-});
-
-var count = 0.0;
-var overallCount = 0.0;
-var totalFailures = 0.0;
-var totalOverallFailures = 0.0;
-var totalMs = 0;
-var totalData = 0;
-var devices = {};
-var lastBracketTs = 0;
-
-rl.on('line',function(line) {
- line = line.trim();
- var ls = line.split(',');
- if (ls.length == 7) {
- var ts = parseInt(ls[0]);
- var fromId = ls[1];
- var toId = ls[2];
- var ms = parseFloat(ls[3]);
- var bytes = parseInt(ls[4]);
- var timedOut = (ls[5] == 'true') ? true : false;
- var errMsg = ls[6];
-
- count += 1.0;
- overallCount += 1.0;
- if ((bytes !== EXPECTED_BYTES)||(timedOut)) {
- totalFailures += 1.0;
- totalOverallFailures += 1.0;
- }
- totalMs += ms;
- totalData += bytes;
-
- devices[fromId] = true;
- devices[toId] = true;
-
- if (lastBracketTs === 0)
- lastBracketTs = ts;
-
- if (((ts - lastBracketTs) >= BRACKET_SIZE)&&(count > 0.0)) {
- console.log(count.toString()+','+overallCount.toString()+','+(totalMs / count)+','+(totalFailures / count)+','+(totalOverallFailures / overallCount)+','+totalData+','+Object.keys(devices).length);
-
- count = 0.0;
- totalFailures = 0.0;
- totalMs = 0;
- totalData = 0;
- lastBracketTs = ts;
- }
- } // else ignore junk
-});
diff --git a/attic/big-http-test/docker-main.sh b/attic/big-http-test/docker-main.sh
deleted file mode 100755
index 29cdced9..00000000
--- a/attic/big-http-test/docker-main.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/bash
-
-export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin
-
-/zerotier-one -d >>zerotier-one.out 2>&1
-
-# Wait for ZeroTier to start and join the network
-while [ ! -d "/proc/sys/net/ipv6/conf/zt0" ]; do
- sleep 0.25
-done
-
-# Wait just a bit longer for stuff to settle
-sleep 5
-
-exec node --harmony /agent.js >>agent.out 2>&1
-#exec node --harmony /agent.js
diff --git a/attic/big-http-test/nodesource-el.repo b/attic/big-http-test/nodesource-el.repo
deleted file mode 100644
index b785d3d0..00000000
--- a/attic/big-http-test/nodesource-el.repo
+++ /dev/null
@@ -1,6 +0,0 @@
-[nodesource]
-name=Node.js Packages for Enterprise Linux 7 - $basearch
-baseurl=https://rpm.nodesource.com/pub_4.x/el/7/$basearch
-failovermethod=priority
-enabled=1
-gpgcheck=0
diff --git a/attic/big-http-test/package.json b/attic/big-http-test/package.json
deleted file mode 100644
index 173a6f99..00000000
--- a/attic/big-http-test/package.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "name": "zerotier-test-http",
- "version": "1.0.0",
- "description": "ZeroTier in-network HTTP test",
- "main": "agent.js",
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
- },
- "author": "ZeroTier, Inc.",
- "license": "GPL-3.0",
- "dependencies": {
- "async": "^1.5.0",
- "express": "^4.13.3",
- "ipaddr.js": "^1.0.3"
- }
-}
diff --git a/attic/big-http-test/server.js b/attic/big-http-test/server.js
deleted file mode 100644
index 629784da..00000000
--- a/attic/big-http-test/server.js
+++ /dev/null
@@ -1,53 +0,0 @@
-// ZeroTier distributed HTTP test coordinator and result-reporting server
-
-// ---------------------------------------------------------------------------
-// Customizable parameters:
-
-var SERVER_PORT = 18080;
-
-// ---------------------------------------------------------------------------
-
-var fs = require('fs');
-
-var express = require('express');
-var app = express();
-
-app.use(function(req,res,next) {
- req.rawBody = '';
- req.on('data', function(chunk) { req.rawBody += chunk.toString(); });
- req.on('end', function() { return next(); });
-});
-
-var knownAgents = {};
-
-app.post('/:agentId',function(req,res) {
- var agentId = req.params.agentId;
- if ((!agentId)||(agentId.length !== 32))
- return res.status(404).send('');
-
- if (req.rawBody) {
- var receiveTime = Date.now();
- var resultData = null;
- try {
- resultData = JSON.parse(req.rawBody);
- console.log(Date.now().toString()+','+resultData.source+','+resultData.target+','+resultData.time+','+resultData.bytes+','+resultData.timedOut+',"'+((resultData.error) ? resultData.error : '')+'"');
- } catch (e) {}
- }
-
- knownAgents[agentId] = true;
- var thisUpdate = [];
- var agents = Object.keys(knownAgents);
- if (agents.length < 100)
- thisUpdate = agents;
- else {
- for(var xx=0;xx<100;++xx)
- thisUpdate.push(agents[Math.floor(Math.random() * agents.length)]);
- }
-
- return res.status(200).send(JSON.stringify(thisUpdate));
-});
-
-var expressServer = app.listen(SERVER_PORT,function () {
- console.log('LISTENING ON '+SERVER_PORT);
- console.log('');
-});
diff --git a/attic/cli/README.md b/attic/cli/README.md
deleted file mode 100644
index 595df07e..00000000
--- a/attic/cli/README.md
+++ /dev/null
@@ -1,57 +0,0 @@
-The new ZeroTier CLI!
-====
-
-With this update we've expanded upon the previous CLI's functionality, so things should seem pretty familiar. Here are some of the new features we've introduced:
-
- - Create and administer networks on ZeroTier Central directly from the console.
- - Service configurations, allows you to control local/remote instances of ZeroTier One
- - Identity generation and management is now part of the same CLI tool
-
-***
-## Configurations
-
-Configurations are a way for you to nickname and logically organize the control of ZeroTier services running locally or remotely (this includes ZeroTier Central!). They're merely groupings of service API url's and auth tokens. The CLI's settings data is contained within `.zerotierCliSettings`.
-
-For instance, you can control your local instance of ZeroTier One via the `@local` config. By default it is represented as follows:
-
-```
-"local": {
- "auth": "7tyqRoFytajf21j2l2t9QPm5",
- "type": "one",
- "url": "http://127.0.0.1:9993/"
-}
-```
-
-As an example, if you issue the command `zerotier ls` is it implicitly stating `zerotier @local ls`.
-
-With the same line of thinking, you could create a `@my.zerotier.com` which would allow for something like `zerotier @my.zerotier.com net-create` which talks to our hosted ZeroTier Central to create a new network.
-
-
-
-## Command families
-
-- `cli-` is for configuring the settings data for the CLI itself, such as adding/removing `@thing` configurations, variables, etc.
-- `net-` is for operating on a *ZeroTier Central* service such as `https://my.zerotier.com`
-- `id-` is for handling ZeroTier identities.
-
-And those commands with no prefix are there to allow you to operate ZeroTier One instances either local or remote.
-
-***
-## Useful command examples
-
-*Add a ZeroTier One configuration:*
-
- - `zerotier cli-add-zt MyLocalConfigName https://127.0.0.1:9993/ <authtoken>`
-
-*Add a ZeroTier Central configuration:*
-
- - `zerotier cli-add-central MyZTCentralConfigName https://my.zerotier.com/ <centralAPIAuthtoken>`
-
-*Set a default ZeroTier One instance:*
-
- - `zerotier cli-set defaultOne MyLocalConfigName`
-
-*Set a default ZeroTier Central:*
-
- - `zerotier cli-set defaultCentral MyZTCentralConfigName`
-
diff --git a/attic/cli/zerotier.cpp b/attic/cli/zerotier.cpp
deleted file mode 100644
index e75268d1..00000000
--- a/attic/cli/zerotier.cpp
+++ /dev/null
@@ -1,957 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-// Note: unlike the rest of ZT's code base, this requires C++11 due to
-// the JSON library it uses and other things.
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-
-#include "../node/Constants.hpp"
-#include "../node/Identity.hpp"
-#include "../version.h"
-#include "../osdep/OSUtils.hpp"
-#include "../ext/offbase/json/json.hpp"
-
-#ifdef __WINDOWS__
-#include <WinSock2.h>
-#include <windows.h>
-#include <tchar.h>
-#include <wchar.h>
-#else
-#include <ctype.h>
-#include <unistd.h>
-#endif
-
-#include <iostream>
-#include <string>
-#include <map>
-#include <vector>
-#include <tuple>
-#include <regex>
-
-#include <curl/curl.h>
-
-using json = nlohmann::json;
-using namespace ZeroTier;
-
-#define ZT_CLI_FLAG_VERBOSE 'v'
-#define ZT_CLI_FLAG_UNSAFE_SSL 'X'
-
-#define REQ_GET 0
-#define REQ_POST 1
-#define REQ_DEL 2
-
-#define OK_STR "[OK ]: "
-#define FAIL_STR "[FAIL]: "
-#define WARN_STR "[WARN]: "
-#define INVALID_ARGS_STR "Invalid args. Usage: "
-
-struct CLIState
-{
- std::string atname;
- std::string command;
- std::string url;
- std::map<std::string,std::string> reqHeaders;
- std::vector<std::string> args;
- std::map<char,std::string> opts;
- json settings;
-};
-
-namespace {
-
-static Identity getIdFromArg(char *arg)
-{
- Identity id;
- if ((strlen(arg) > 32)&&(arg[10] == ':')) { // identity is a literal on the command line
- if (id.fromString(arg))
- return id;
- } else { // identity is to be read from a file
- std::string idser;
- if (OSUtils::readFile(arg,idser)) {
- if (id.fromString(idser))
- return id;
- }
- }
- return Identity();
-}
-
-static std::string trimString(const std::string &s)
-{
- unsigned long end = (unsigned long)s.length();
- while (end) {
- char c = s[end - 1];
- if ((c == ' ')||(c == '\r')||(c == '\n')||(!c)||(c == '\t'))
- --end;
- else break;
- }
- unsigned long start = 0;
- while (start < end) {
- char c = s[start];
- if ((c == ' ')||(c == '\r')||(c == '\n')||(!c)||(c == '\t'))
- ++start;
- else break;
- }
- return s.substr(start,end - start);
-}
-
-static inline std::string getSettingsFilePath()
-{
-#ifdef __WINDOWS__
-#else
- const char *home = getenv("HOME");
- if (!home)
- home = "/";
- return (std::string(home) + "/.zerotierCliSettings");
-#endif
-}
-
-static bool saveSettingsBackup(CLIState &state)
-{
- std::string sfp(getSettingsFilePath().c_str());
- if(state.settings.find("generateBackupConfig") != state.settings.end()
- && state.settings["generateBackupConfig"].get<std::string>() == "true") {
- std::string backup_file = getSettingsFilePath() + ".bak";
- if(!OSUtils::writeFile(sfp.c_str(), state.settings.dump(2))) {
- OSUtils::lockDownFile(sfp.c_str(),false);
- std::cout << WARN_STR << "unable to write backup config file" << std::endl;
- return false;
- }
- return true;
- }
- return false;
-}
-
-static bool saveSettings(CLIState &state)
-{
- std::string sfp(getSettingsFilePath().c_str());
- if(OSUtils::writeFile(sfp.c_str(), state.settings.dump(2))) {
- OSUtils::lockDownFile(sfp.c_str(),false);
- std::cout << OK_STR << "changes saved." << std::endl;
- return true;
- }
- std::cout << FAIL_STR << "unable to write to " << sfp << std::endl;
- return false;
-}
-
-static void dumpHelp()
-{
- std::cout << "ZeroTier Newer-Spiffier CLI " << ZEROTIER_ONE_VERSION_MAJOR << "." << ZEROTIER_ONE_VERSION_MINOR << "." << ZEROTIER_ONE_VERSION_REVISION << std::endl;
- std::cout << "(c)2016 ZeroTier, Inc. / Licensed under the GNU GPL v3" << std::endl;
- std::cout << std::endl;
- std::cout << "Configuration path: " << getSettingsFilePath() << std::endl;
- std::cout << std::endl;
- std::cout << "Usage: zerotier [-option] [@name] <command> [<command options>]" << std::endl;
- std::cout << std::endl;
- std::cout << "Options:" << std::endl;
- std::cout << " -verbose - Verbose JSON output" << std::endl;
- std::cout << " -X - Do not check SSL certs (CAUTION!)" << std::endl;
- std::cout << std::endl;
- std::cout << "CLI Configuration Commands:" << std::endl;
- std::cout << " cli-set <setting> <value> - Set a CLI option ('cli-set help')" << std::endl;
- std::cout << " cli-unset <setting> <value> - Un-sets a CLI option ('cli-unset help')" << std::endl;
- std::cout << " cli-ls - List configured @things" << std::endl;
- std::cout << " cli-rm @name - Remove a configured @thing" << std::endl;
- std::cout << " cli-add-zt @name <url> <auth> - Add a ZeroTier service" << std::endl;
- std::cout << " cli-add-central @name <url> <auth> - Add ZeroTier Central instance" << std::endl;
- std::cout << std::endl;
- std::cout << "ZeroTier One Service Commands:" << std::endl;
- std::cout << " -v / -version - Displays default local instance's version'" << std::endl;
- std::cout << " ls - List currently joined networks" << std::endl;
- std::cout << " join <network> [opt=value ...] - Join a network" << std::endl;
- std::cout << " leave <network> - Leave a network" << std::endl;
- std::cout << " peers - List ZeroTier VL1 peers" << std::endl;
- std::cout << " show [<network/peer address>] - Get info about self or object" << std::endl;
- std::cout << std::endl;
- std::cout << "Network Controller Commands:" << std::endl;
- std::cout << " net-create - Create a new network" << std::endl;
- std::cout << " net-rm <network> - Delete a network (CAUTION!)" << std::endl;
- std::cout << " net-ls - List administered networks" << std::endl;
- std::cout << " net-members <network> - List members of a network" << std::endl;
- std::cout << " net-show <network> [<address>] - Get network or member info" << std::endl;
- std::cout << " net-auth <network> <address> - Authorize a member" << std::endl;
- std::cout << " net-unauth <network> <address> - De-authorize a member" << std::endl;
- std::cout << " net-set <path> <value> - See 'net-set help'" << std::endl;
- std::cout << std::endl;
- std::cout << "Identity Commands:" << std::endl;
- std::cout << " id-generate [<vanity prefix>] - Generate a ZeroTier identity" << std::endl;
- std::cout << " id-validate <identity> - Locally validate an identity" << std::endl;
- std::cout << " id-sign <identity> <file> - Sign a file" << std::endl;
- std::cout << " id-verify <secret> <file> <sig> - Verify a file's signature" << std::endl;
- std::cout << " id-getpublic <secret> - Get full identity's public portion" << std::endl;
- std::cout << std::endl;
-}
-
-static size_t _curlStringAppendCallback(void *contents,size_t size,size_t nmemb,void *stdstring)
-{
- size_t totalSize = size * nmemb;
- reinterpret_cast<std::string *>(stdstring)->append((const char *)contents,totalSize);
- return totalSize;
-}
-
-static std::tuple<int,std::string> REQUEST(int requestType, CLIState &state, const std::map<std::string,std::string> &headers, const std::string &postfield, const std::string &url)
-{
- std::string body;
- char errbuf[CURL_ERROR_SIZE];
- char urlbuf[4096];
-
- CURL *curl;
- curl = curl_easy_init();
- if (!curl) {
- std::cerr << "FATAL: curl_easy_init() failed" << std::endl;
- exit(-1);
- }
-
- Utils::scopy(urlbuf,sizeof(urlbuf),url.c_str());
- curl_easy_setopt(curl,CURLOPT_URL,urlbuf);
-
- struct curl_slist *hdrs = (struct curl_slist *)0;
- for(std::map<std::string,std::string>::const_iterator i(headers.begin());i!=headers.end();++i) {
- std::string htmp(i->first);
- htmp.append(": ");
- htmp.append(i->second);
- hdrs = curl_slist_append(hdrs,htmp.c_str());
- }
- if (hdrs)
- curl_easy_setopt(curl,CURLOPT_HTTPHEADER,hdrs);
-
- //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
- curl_easy_setopt(curl,CURLOPT_WRITEDATA,(void *)&body);
- curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,_curlStringAppendCallback);
-
- if(std::find(state.args.begin(), state.args.end(), "-X") == state.args.end())
- curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,(state.opts.count(ZT_CLI_FLAG_UNSAFE_SSL) > 0) ? 0L : 1L);
-
- if(requestType == REQ_POST) {
- curl_easy_setopt(curl, CURLOPT_POST, 1);
- curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postfield.c_str());
- }
- if(requestType == REQ_DEL)
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
- if(requestType == REQ_GET) {
- curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errbuf);
- curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,0L);
- }
-
- curl_easy_setopt(curl,CURLOPT_USERAGENT,"ZeroTier-CLI");
- CURLcode res = curl_easy_perform(curl);
-
- errbuf[CURL_ERROR_SIZE-1] = (char)0; // sanity check
-
- if (res != CURLE_OK)
- return std::make_tuple(-1,std::string(errbuf));
-
- long response_code;
- int rc = (int)curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE, &response_code);
-
- if(response_code == 401) { std::cout << FAIL_STR << response_code << "Unauthorized." << std::endl; exit(0); }
- else if(response_code == 403) { std::cout << FAIL_STR << response_code << "Forbidden." << std::endl; exit(0); }
- else if(response_code == 404) { std::cout << FAIL_STR << response_code << "Not found." << std::endl; exit(0); }
- else if(response_code == 408) { std::cout << FAIL_STR << response_code << "Request timed out." << std::endl; exit(0); }
- else if(response_code != 200) { std::cout << FAIL_STR << response_code << "Unable to process request." << std::endl; exit(0); }
-
- curl_easy_cleanup(curl);
- if (hdrs)
- curl_slist_free_all(hdrs);
- return std::make_tuple(response_code,body);
-}
-
-} // anonymous namespace
-
-//////////////////////////////////////////////////////////////////////////////
-
-// Check for user-specified @thing config
-// Make sure it @thing makes sense
-// Apply appropriate request headers
-static void checkForThing(CLIState &state, std::string thingType, bool warnNoThingProvided)
-{
- std::string configName;
- if(state.atname.length()) {
- configName = state.atname.erase(0,1);
- // make sure specified @thing makes sense in the context of the command
- if(thingType == "one" && state.settings["things"][configName]["type"].get<std::string>() != "one") {
- std::cout << FAIL_STR << "A ZeroTier Central config was specified for a ZeroTier One command." << std::endl;
- exit(0);
- }
- if(thingType == "central" && state.settings["things"][configName]["type"].get<std::string>() != "central") {
- std::cout << FAIL_STR << "A ZeroTier One config was specified for a ZeroTier Central command." << std::endl;
- exit(0);
- }
- }
- else { // no @thing specified, check for defaults depending on type
- if(thingType == "one") {
- if(state.settings.find("defaultOne") != state.settings.end()) {
- if(warnNoThingProvided)
- std::cout << WARN_STR << "No @thing specified, assuming default for ZeroTier One command: " << state.settings["defaultOne"].get<std::string>().c_str() << std::endl;
- configName = state.settings["defaultOne"].get<std::string>().erase(0,1); // get default
- }
- else {
- std::cout << WARN_STR << "No @thing specified, and no default is known." << std::endl;
- std::cout << "HELP: To set a default: zerotier cli-set defaultOne @my_default_thing" << std::endl;
- exit(0);
- }
- }
- if(thingType == "central") {
- if(state.settings.find("defaultCentral") != state.settings.end()) {
- if(warnNoThingProvided)
- std::cout << WARN_STR << "No @thing specified, assuming default for ZeroTier Central command: " << state.settings["defaultCentral"].get<std::string>().c_str() << std::endl;
- configName = state.settings["defaultCentral"].get<std::string>().erase(0,1); // get default
- }
- else {
- std::cout << WARN_STR << "No @thing specified, and no default is known." << std::endl;
- std::cout << "HELP: To set a default: zerotier cli-set defaultCentral @my_default_thing" << std::endl;
- exit(0);
- }
- }
- }
- // Apply headers
- if(thingType == "one") {
- state.reqHeaders["X-ZT1-Auth"] = state.settings["things"][configName]["auth"];
- }
- if(thingType == "central"){
- state.reqHeaders["Content-Type"] = "application/json";
- state.reqHeaders["Authorization"] = "Bearer " + state.settings["things"][configName]["auth"].get<std::string>();
- state.reqHeaders["Accept"] = "application/json";
- }
- state.url = state.settings["things"][configName]["url"];
-}
-
-static bool checkURL(std::string url)
-{
- // TODO
- return true;
-}
-
-static std::string getLocalVersion(CLIState &state)
-{
- json result;
- std::tuple<int,std::string> res;
- checkForThing(state,"one",false);
- res = REQUEST(REQ_GET,state,state.reqHeaders,"",state.url + "/status");
- if(std::get<0>(res) == 200) {
- result = json::parse(std::get<1>(res));
- return result["version"].get<std::string>();
- }
- return "---";
-}
-
-#ifdef __WINDOWS__
-int _tmain(int argc, _TCHAR* argv[])
-#else
-int main(int argc,char **argv)
-#endif
-{
-#ifdef __WINDOWS__
- {
- WSADATA wsaData;
- WSAStartup(MAKEWORD(2,2),&wsaData);
- }
-#endif
-
- curl_global_init(CURL_GLOBAL_DEFAULT);
- CLIState state;
- std::string arg1, arg2, authToken;
-
- for(int i=1;i<argc;++i) {
- if (argv[i][0] == '@') {
- state.atname = argv[i];
- }
- else if (state.command.length() == 0) {
- if (argv[i][0] == '-') {
- if (!argv[i][1]) {
- dumpHelp();
- return -1;
- } else if (argv[i][2]) {
- state.opts[argv[i][1]] = argv[i] + 2;
- } else {
- state.opts[argv[i][1]] = "";
- }
- } else {
- state.command = argv[i];
- }
- }
- else {
- state.args.push_back(std::string(argv[i]));
- }
- }
-
- {
- std::string buf;
- if (OSUtils::readFile(getSettingsFilePath().c_str(),buf))
- state.settings = json::parse(buf);
-
- if (state.settings.empty()) {
- // Default settings
- state.settings = {
- { "configVersion", 1 },
- { "things", {
- { "my.zerotier.com", {
- { "type", "central" },
- { "url", "https://my.zerotier.com/" },
- { "auth", "" }
- }},
- { "local", {
- { "type", "one" },
- { "url", "" },
- { "auth", "" }
- }}
- }},
- { "defaultController", "@my.zerotier.com" },
- { "defaultOne", "@local" }
- };
-
- std::string oneHome(OSUtils::platformDefaultHomePath());
- std::string portStr;
- bool initSuccess = false;
- std::string path = oneHome + ZT_PATH_SEPARATOR_S ;
- if (OSUtils::readFile((oneHome + ZT_PATH_SEPARATOR_S + "authtoken.secret").c_str(),authToken)&&OSUtils::readFile((oneHome + ZT_PATH_SEPARATOR_S + "zerotier-one.port").c_str(),portStr)) {
- portStr = trimString(portStr);
- authToken = trimString(authToken);
- int port = Utils::strToInt(portStr.c_str());
- if (((port > 0)&&(port < 65536))&&(authToken.length() > 0)) {
- state.settings["things"]["local"]["url"] = (std::string("http://127.0.0.1:") + portStr + "/");
- state.settings["things"]["local"]["auth"] = authToken;
- initSuccess = true;
- }
- }
-
- if (!saveSettings(state)) {
- std::cerr << "FATAL: unable to write " << getSettingsFilePath() << std::endl;
- exit(-1);
- }
-
- if (initSuccess) {
- std::cerr << "INFO: initialized new config at " << getSettingsFilePath() << std::endl;
- } else {
- std::cerr << "INFO: initialized new config at " << getSettingsFilePath() << " but could not auto-init local ZeroTier One service config from " << oneHome << " -- you will need to set local service URL and port manually if you want to control a local instance of ZeroTier One. (This happens if you are not root/administrator.)" << std::endl;
- }
- }
- }
-
- // PRE-REQUEST SETUP
-
- json result;
- std::tuple<int,std::string> res;
- std::string url = "";
-
- // META
-
- if ((state.command.length() == 0)||(state.command == "help")) {
- dumpHelp();
- return -1;
- }
-
- // zerotier version
- else if (state.command == "v" || state.command == "version") {
- std::cout << getLocalVersion(state) << std::endl;
- return 1;
- }
-
- // zerotier cli-set <setting> <value>
- else if (state.command == "cli-set") {
- if(argc != 4) {
- std::cerr << INVALID_ARGS_STR << "zerotier cli-set <setting> <value>" << std::endl;
- return 1;
- }
- std::string settingName, settingValue;
- if(state.atname.length()) { // User provided @thing erroneously, we will ignore it and adjust argument indices
- settingName = argv[3];
- settingValue = argv[4];
- }
- else {
- settingName = argv[2];
- settingValue = argv[3];
- }
- saveSettingsBackup(state);
- state.settings[settingName] = settingValue; // changes
- saveSettings(state);
- }
-
- // zerotier cli-unset <setting>
- else if (state.command == "cli-unset") {
- if(argc != 3) {
- std::cerr << INVALID_ARGS_STR << "zerotier cli-unset <setting>" << std::endl;
- return 1;
- }
- std::string settingName;
- if(state.atname.length()) // User provided @thing erroneously, we will ignore it and adjust argument indices
- settingName = argv[3];
- else
- settingName = argv[2];
- saveSettingsBackup(state);
- state.settings.erase(settingName); // changes
- saveSettings(state);
- }
-
- // zerotier @thing_to_remove cli-rm --- removes the configuration
- else if (state.command == "cli-rm") {
- if(argc != 3) {
- std::cerr << INVALID_ARGS_STR << "zerotier cli-rm <@thing>" << std::endl;
- return 1;
- }
- if(state.settings["things"].find(state.atname) != state.settings["things"].end()) {
- if(state.settings["defaultOne"] == state.atname) {
- std::cout << "WARNING: The config you're trying to remove is currently set as your default. Set a new default first!" << std::endl;
- std::cout << " | Usage: zerotier set defaultOne @your_other_thing" << std::endl;
- }
- else {
- state.settings["things"].erase(state.atname.c_str());
- saveSettings(state);
- }
- }
- }
-
- // zerotier cli-add-zt <shortname> <url> <auth>
- // TODO: Check for malformed urls/auth
- else if (state.command == "cli-add-zt") {
- if(argc != 5) {
- std::cerr << INVALID_ARGS_STR << "zerotier cli-add-zt <shortname> <url> <authToken>" << std::endl;
- return 1;
- }
- std::string thing_name = argv[2], url = argv[3], auth = argv[4];
- if(!checkURL(url)) {
- std::cout << FAIL_STR << "Malformed URL" << std::endl;
- return 1;
- }
- if(state.settings.find(thing_name) != state.settings.end()) {
- std::cout << "WARNING: A @thing with the shortname " << thing_name.c_str()
- << " already exists. Choose another name or rename the old @thing" << std::endl;
- std::cout << " | Usage: To rename a @thing: zerotier cli-rename @old_thing_name @new_thing_name" << std::endl;
- }
- else {
- result = json::parse("{ \"auth\": \"" + auth + "\", \"type\": \"" + "one" + "\", \"url\": \"" + url + "\" }");
- saveSettingsBackup(state);
- // TODO: Handle cases where user may or may not prepend an @
- state.settings["things"][thing_name] = result; // changes
- saveSettings(state);
- }
- }
-
- // zerotier cli-add-central <shortname> <url> <auth>
- // TODO: Check for malformed urls/auth
- else if (state.command == "cli-add-central") {
- if(argc != 5) {
- std::cerr << INVALID_ARGS_STR << "zerotier cli-add-central <shortname> <url> <authToken>" << std::endl;
- return 1;
- }
- std::string thing_name = argv[2], url = argv[3], auth = argv[4];
- if(!checkURL(url)) {
- std::cout << FAIL_STR << "Malformed URL" << std::endl;
- return 1;
- }
- if(state.settings.find(thing_name) != state.settings.end()) {
- std::cout << "WARNING: A @thing with the shortname " << thing_name.c_str()
- << " already exists. Choose another name or rename the old @thing" << std::endl;
- std::cout << " | Usage: To rename a @thing: zerotier cli-rename @old_thing_name @new_thing_name" << std::endl;
- }
- else {
- result = json::parse("{ \"auth\": \"" + auth + "\", \"type\": \"" + "central" + "\", \"url\": \"" + url + "\" }");
- saveSettingsBackup(state);
- // TODO: Handle cases where user may or may not prepend an @
- state.settings["things"]["@" + thing_name] = result; // changes
- saveSettings(state);
- }
- }
-
- // ONE SERVICE
-
- // zerotier ls --- display all networks currently joined
- else if (state.command == "ls" || state.command == "listnetworks") {
- if(argc != 2) {
- std::cerr << INVALID_ARGS_STR << "zerotier ls" << std::endl;
- return 1;
- }
- checkForThing(state,"one",true);
- url = state.url + "network";
- res = REQUEST(REQ_GET,state,state.reqHeaders,"",(const std::string)url);
- if(std::get<0>(res) == 200) {
- std::cout << "listnetworks <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips>" << std::endl;
- auto j = json::parse(std::get<1>(res).c_str());
- if (j.type() == json::value_t::array) {
- for(int i=0;i<j.size();i++){
- std::string nwid = j[i]["nwid"].get<std::string>();
- std::string name = j[i]["name"].get<std::string>();
- std::string mac = j[i]["mac"].get<std::string>();
- std::string status = j[i]["status"].get<std::string>();
- std::string type = j[i]["type"].get<std::string>();
- std::string addrs;
- for(int m=0; m<j[i]["assignedAddresses"].size(); m++) {
- addrs += j[i]["assignedAddresses"][m].get<std::string>() + " ";
- }
- std::string dev = j[i]["portDeviceName"].get<std::string>();
- std::cout << "listnetworks " << nwid << " " << name << " " << mac << " " << status << " " << type << " " << dev << " " << addrs << std::endl;
- }
- }
- }
- }
-
- // zerotier join <nwid> --- joins a network
- else if (state.command == "join") {
- if(argc != 3) {
- std::cerr << INVALID_ARGS_STR << "zerotier join <nwid>" << std::endl;
- return 1;
- }
- checkForThing(state,"one",true);
- res = REQUEST(REQ_POST,state,state.reqHeaders,"{}",state.url + "/network/" + state.args[0]);
- if(std::get<0>(res) == 200) {
- std::cout << OK_STR << "connected to " << state.args[0] << std::endl;
- }
- }
-
- // zerotier leave <nwid> --- leaves a network
- else if (state.command == "leave") {
- if(argc != 3) {
- std::cerr << INVALID_ARGS_STR << "zerotier leave <nwid>" << std::endl;
- return 1;
- }
- checkForThing(state,"one",true);
- res = REQUEST(REQ_DEL,state,state.reqHeaders,"{}",state.url + "/network/" + state.args[0]);
- if(std::get<0>(res) == 200) {
- std::cout << OK_STR << "disconnected from " << state.args[0] << std::endl;
- }
- }
-
- // zerotier peers --- display address and role of all peers
- else if (state.command == "peers") {
- if(argc != 2) {
- std::cerr << INVALID_ARGS_STR << "zerotier peers" << std::endl;
- return 1;
- }
- checkForThing(state,"one",true);
- res = REQUEST(REQ_GET,state,state.reqHeaders,"",state.url + "/peer");
- if(std::get<0>(res) == 200) {
- json result = json::parse(std::get<1>(res));
- for(int i=0; i<result.size(); i++) {
- std::cout << result[i]["address"] << " " << result[i]["role"] << std::endl;
- }
- }
- }
-
- // zerotier show --- display status of local instance
- else if (state.command == "show" || state.command == "status") {
- if(argc != 2) {
- std::cerr << INVALID_ARGS_STR << "zerotier show" << std::endl;
- return 1;
- }
- checkForThing(state,"one",true);
- res = REQUEST(REQ_GET,state,state.reqHeaders,"",state.url + "/status");
- if(std::get<0>(res) == 200) {
- result = json::parse(std::get<1>(res));
- std::string status_str = result["online"].get<bool>() ? "ONLINE" : "OFFLINE";
- std::cout << "info " << result["address"].get<std::string>()
- << " " << status_str << " " << result["version"].get<std::string>() << std::endl;
- }
- }
-
- // REMOTE
-
- // zerotier @thing net-create --- creates a new network
- else if (state.command == "net-create") {
- if(argc > 3 || (argc == 3 && !state.atname.length())) {
- std::cerr << INVALID_ARGS_STR << "zerotier <@thing> net-create" << std::endl;
- return 1;
- }
- checkForThing(state,"central",true);
- res = REQUEST(REQ_POST,state,state.reqHeaders,"",state.url + "api/network");
- if(std::get<0>(res) == 200) {
- json result = json::parse(std::get<1>(res));
- std::cout << OK_STR << "created network " << result["config"]["nwid"].get<std::string>() << std::endl;
- }
- }
-
- // zerotier @thing net-rm <nwid> --- deletes a network
- else if (state.command == "net-rm") {
- if(argc > 4 || (argc == 4 && !state.atname.length())) {
- std::cerr << INVALID_ARGS_STR << "zerotier <@thing> net-rm <nwid>" << std::endl;
- return 1;
- }
- checkForThing(state,"central",true);
- if(!state.args.size()) {
- std::cout << "Argument error: No network specified." << std::endl;
- std::cout << " | Usage: zerotier net-rm <nwid>" << std::endl;
- }
- else {
- std::string nwid = state.args[0];
- res = REQUEST(REQ_DEL,state,state.reqHeaders,"",state.url + "api/network/" + nwid);
- if(std::get<0>(res) == 200) {
- std::cout << "deleted network " << nwid << std::endl;
- }
- }
- }
-
- // zerotier @thing net-ls --- lists all networks
- else if (state.command == "net-ls") {
- if(argc > 3 || (argc == 3 && !state.atname.length())) {
- std::cerr << INVALID_ARGS_STR << "zerotier <@thing> net-ls" << std::endl;
- return 1;
- }
- checkForThing(state,"central",true);
- res = REQUEST(REQ_GET,state,state.reqHeaders,"",state.url + "api/network");
- if(std::get<0>(res) == 200) {
- json result = json::parse(std::get<1>(res));
- for(int m=0;m<result.size(); m++) {
- std::cout << "network " << result[m]["id"].get<std::string>() << std::endl;
- }
- }
- }
-
- // zerotier @thing net-members <nwid> --- show all members of a network
- else if (state.command == "net-members") {
- if(argc > 4 || (argc == 4 && !state.atname.length())) {
- std::cerr << INVALID_ARGS_STR << "zerotier <@thing> net-members <nwid>" << std::endl;
- return 1;
- }
- checkForThing(state,"central",true);
- if(!state.args.size()) {
- std::cout << FAIL_STR << "Argument error: No network specified." << std::endl;
- std::cout << " | Usage: zerotier net-members <nwid>" << std::endl;
- }
- else {
- std::string nwid = state.args[0];
- res = REQUEST(REQ_GET,state,state.reqHeaders,"",state.url + "api/network/" + nwid + "/member");
- json result = json::parse(std::get<1>(res));
- std::cout << "Members of " << nwid << ":" << std::endl;
- for (json::iterator it = result.begin(); it != result.end(); ++it) {
- std::cout << it.key() << std::endl;
- }
- }
- }
-
- // zerotier @thing net-show <nwid> <devID> --- show info about a device on a specific network
- else if (state.command == "net-show") {
- if(argc > 5 || (argc == 5 && !state.atname.length())) {
- std::cerr << INVALID_ARGS_STR << "zerotier <@thing> net-show <nwid> <devID>" << std::endl;
- return 1;
- }
- checkForThing(state,"central",true);
- if(state.args.size() < 2) {
- std::cout << FAIL_STR << "Argument error: Too few arguments." << std::endl;
- std::cout << " | Usage: zerotier net-show <nwid> <devID>" << std::endl;
- }
- else {
- std::string nwid = state.args[0];
- std::string devid = state.args[1];
- res = REQUEST(REQ_GET,state,state.reqHeaders,"",state.url + "api/network/" + nwid + "/member/" + devid);
- // TODO: More info, what would we like to show exactly?
- if(std::get<0>(res) == 200) {
- json result = json::parse(std::get<1>(res));
- std::cout << "Assigned IP: " << std::endl;
- for(int m=0; m<result["config"]["ipAssignments"].size();m++) {
- std::cout << "\t" << result["config"]["ipAssignments"][m].get<std::string>() << std::endl;
- }
- }
- }
- }
-
- // zerotier @thing net-auth <nwid> <devID> --- authorize a device on a network
- else if (state.command == "net-auth") {
- if(argc > 5 || (argc == 5 && !state.atname.length())) {
- std::cerr << INVALID_ARGS_STR << "zerotier <@thing> net-auth <nwid> <devID>" << std::endl;
- return 1;
- }
- checkForThing(state,"central",true);
- if(state.args.size() != 2) {
- std::cout << FAIL_STR << "Argument error: Network and/or device ID not specified." << std::endl;
- std::cout << " | Usage: zerotier net-auth <nwid> <devID>" << std::endl;
- }
- std::string nwid = state.args[0];
- std::string devid = state.args[1];
- url = state.url + "api/network/" + nwid + "/member/" + devid;
- // Add device to network
- res = REQUEST(REQ_POST,state,state.reqHeaders,"",(const std::string)url);
- if(std::get<0>(res) == 200) {
- result = json::parse(std::get<1>(res));
- res = REQUEST(REQ_GET,state,state.reqHeaders,"",(const std::string)url);
- result = json::parse(std::get<1>(res));
- result["config"]["authorized"] = "true";
- std::string newconfig = result.dump();
- res = REQUEST(REQ_POST,state,state.reqHeaders,newconfig,(const std::string)url);
- if(std::get<0>(res) == 200)
- std::cout << OK_STR << devid << " authorized on " << nwid << std::endl;
- else
- std::cout << FAIL_STR << "There was a problem authorizing that device." << std::endl;
- }
- }
-
- // zerotier @thing net-unauth <nwid> <devID>
- else if (state.command == "net-unauth") {
- if(argc > 5 || (argc == 5 && !state.atname.length())) {
- std::cerr << INVALID_ARGS_STR << "zerotier <@thing> net-unauth <nwid> <devID>" << std::endl;
- return 1;
- }
- checkForThing(state,"central",true);
- if(state.args.size() != 2) {
- std::cout << FAIL_STR << "Bad argument. No network and/or device ID specified." << std::endl;
- std::cout << " | Usage: zerotier net-unauth <nwid> <devID>" << std::endl;
- }
- std::string nwid = state.args[0];
- std::string devid = state.args[1];
- // If successful, get member config
- res = REQUEST(REQ_GET,state,state.reqHeaders,"",state.url + "api/network/" + nwid + "/member/" + devid);
- result = json::parse(std::get<1>(res));
- // modify auth field and re-POST
- result["config"]["authorized"] = "false";
- std::string newconfig = result.dump();
- res = REQUEST(REQ_POST,state,state.reqHeaders,newconfig,state.url + "api/network/" + nwid + "/member/" + devid);
- if(std::get<0>(res) == 200)
- std::cout << OK_STR << devid << " de-authorized from " << nwid << std::endl;
- else
- std::cout << FAIL_STR << "There was a problem de-authorizing that device." << std::endl;
- }
-
- // zerotier @thing net-set
- else if (state.command == "net-set") {
- }
-
- // ID
-
- // zerotier id-generate [<vanity prefix>]
- else if (state.command == "id-generate") {
- if(argc != 3) {
- std::cerr << INVALID_ARGS_STR << "zerotier id-generate [<vanity prefix>]" << std::endl;
- return 1;
- }
- uint64_t vanity = 0;
- int vanityBits = 0;
- if (argc >= 5) {
- vanity = Utils::hexStrToU64(argv[4]) & 0xffffffffffULL;
- vanityBits = 4 * strlen(argv[4]);
- if (vanityBits > 40)
- vanityBits = 40;
- }
-
- ZeroTier::Identity id;
- for(;;) {
- id.generate();
- if ((id.address().toInt() >> (40 - vanityBits)) == vanity) {
- if (vanityBits > 0) {
- fprintf(stderr,"vanity address: found %.10llx !\n",(unsigned long long)id.address().toInt());
- }
- break;
- } else {
- fprintf(stderr,"vanity address: tried %.10llx looking for first %d bits of %.10llx\n",(unsigned long long)id.address().toInt(),vanityBits,(unsigned long long)(vanity << (40 - vanityBits)));
- }
- }
-
- std::string idser = id.toString(true);
- if (argc >= 3) {
- if (!OSUtils::writeFile(argv[2],idser)) {
- std::cerr << "Error writing to " << argv[2] << std::endl;
- return 1;
- } else std::cout << argv[2] << " written" << std::endl;
- if (argc >= 4) {
- idser = id.toString(false);
- if (!OSUtils::writeFile(argv[3],idser)) {
- std::cerr << "Error writing to " << argv[3] << std::endl;
- return 1;
- } else std::cout << argv[3] << " written" << std::endl;
- }
- } else std::cout << idser << std::endl;
- }
-
- // zerotier id-validate <identity>
- else if (state.command == "id-validate") {
- if(argc != 3) {
- std::cerr << INVALID_ARGS_STR << "zerotier id-validate <identity>" << std::endl;
- return 1;
- }
- Identity id = getIdFromArg(argv[2]);
- if (!id) {
- std::cerr << "Identity argument invalid or file unreadable: " << argv[2] << std::endl;
- return 1;
- }
- if (!id.locallyValidate()) {
- std::cerr << argv[2] << " FAILED validation." << std::endl;
- return 1;
- } else std::cout << argv[2] << "is a valid identity" << std::endl;
- }
-
- // zerotier id-sign <identity> <file>
- else if (state.command == "id-sign") {
- if(argc != 4) {
- std::cerr << INVALID_ARGS_STR << "zerotier id-sign <identity> <file>" << std::endl;
- return 1;
- }
- Identity id = getIdFromArg(argv[2]);
- if (!id) {
- std::cerr << "Identity argument invalid or file unreadable: " << argv[2] << std::endl;
- return 1;
- }
- if (!id.hasPrivate()) {
- std::cerr << argv[2] << " does not contain a private key (must use private to sign)" << std::endl;
- return 1;
- }
- std::string inf;
- if (!OSUtils::readFile(argv[3],inf)) {
- std::cerr << argv[3] << " is not readable" << std::endl;
- return 1;
- }
- C25519::Signature signature = id.sign(inf.data(),(unsigned int)inf.length());
- std::cout << Utils::hex(signature.data,(unsigned int)signature.size()) << std::endl;
- }
-
- // zerotier id-verify <secret> <file> <sig>
- else if (state.command == "id-verify") {
- if(argc != 4) {
- std::cerr << INVALID_ARGS_STR << "zerotier id-verify <secret> <file> <sig>" << std::endl;
- return 1;
- }
- Identity id = getIdFromArg(argv[2]);
- if (!id) {
- std::cerr << "Identity argument invalid or file unreadable: " << argv[2] << std::endl;
- return 1;
- }
- std::string inf;
- if (!OSUtils::readFile(argv[3],inf)) {
- std::cerr << argv[3] << " is not readable" << std::endl;
- return 1;
- }
- std::string signature(Utils::unhex(argv[4]));
- if ((signature.length() > ZT_ADDRESS_LENGTH)&&(id.verify(inf.data(),(unsigned int)inf.length(),signature.data(),(unsigned int)signature.length()))) {
- std::cout << argv[3] << " signature valid" << std::endl;
- } else {
- std::cerr << argv[3] << " signature check FAILED" << std::endl;
- return 1;
- }
- }
-
- // zerotier id-getpublic <secret>
- else if (state.command == "id-getpublic") {
- if(argc != 3) {
- std::cerr << INVALID_ARGS_STR << "zerotier id-getpublic <secret>" << std::endl;
- return 1;
- }
- Identity id = getIdFromArg(argv[2]);
- if (!id) {
- std::cerr << "Identity argument invalid or file unreadable: " << argv[2] << std::endl;
- return 1;
- }
- std::cerr << id.toString(false) << std::endl;
- }
- //
- else {
- dumpHelp();
- return -1;
- }
- if(std::find(state.args.begin(), state.args.end(), "-verbose") != state.args.end())
- std::cout << "\n\nAPI response = " << std::get<1>(res) << std::endl;
- curl_global_cleanup();
- return 0;
-}
diff --git a/attic/historic/anode/LICENSE.txt b/attic/historic/anode/LICENSE.txt
new file mode 100644
index 00000000..94a9ed02
--- /dev/null
+++ b/attic/historic/anode/LICENSE.txt
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/attic/historic/anode/config.mk.Darwin b/attic/historic/anode/config.mk.Darwin
new file mode 100644
index 00000000..a5cbeeec
--- /dev/null
+++ b/attic/historic/anode/config.mk.Darwin
@@ -0,0 +1,17 @@
+CC=gcc
+CXX=g++
+
+#ARCH_FLAGS=-arch x86_64 -arch i386 -arch ppc
+
+DEFS=-DHAS_DEV_URANDOM
+CXXDEFS=-DBOOST_DISABLE_ASSERTS -DBOOST_NO_TYPEID -DNDEBUG
+
+CFLAGS=-mmacosx-version-min=10.4 -std=c99 -O6 -ftree-vectorize -Wall $(DEFS) $(ARCH_FLAGS)
+CXXFLAGS=-mmacosx-version-min=10.4 -Drestrict=__restrict__ -O6 -ftree-vectorize -Wall $(DEFS) $(CXXDEFS) $(ARCH_FLAGS)
+
+LDFLAGS=-mmacosx-version-min=10.4 $(ARCH_FLAGS)
+DLLFLAGS=$(ARCH_FLAGS) -shared
+DLLEXT=dylib
+
+LIBANODE_LIBS=-lcrypto -lpthread -lresolv
+LIBSPARK_LIBS=-lz
diff --git a/attic/historic/anode/config.mk.Linux b/attic/historic/anode/config.mk.Linux
new file mode 100644
index 00000000..072fa4ce
--- /dev/null
+++ b/attic/historic/anode/config.mk.Linux
@@ -0,0 +1,17 @@
+CC=gcc
+CXX=g++
+
+DEFS=-DHAS_DEV_URANDOM
+
+CFLAGS=-std=c99 -O6 -fPIC -Wall $(DEFS)
+CXXFLAGS=-Drestrict=__restrict__ -O6 -Wall $(DEFS) -I..
+
+#CFLAGS=-g -Wall $(DEFS)
+#CXXFLAGS=-g -Wall $(DEFS)
+
+LDFLAGS=
+DLLFLAGS=-shared
+DLLEXT=so
+
+LIBANODE_LIBS=-lcrypto -lresolv -pthread
+LIBSPARK_LIBS=-lz
diff --git a/attic/historic/anode/docs/anode_protocol.txt b/attic/historic/anode/docs/anode_protocol.txt
new file mode 100644
index 00000000..0adb9236
--- /dev/null
+++ b/attic/historic/anode/docs/anode_protocol.txt
@@ -0,0 +1,764 @@
+*****************************************************************************
+Anode Protocol Specification Draft
+Version 0.8
+
+(c)2009-2010 Adam Ierymenko
+*****************************************************************************
+
+Table of Contents
+
+*****************************************************************************
+
+1. Introduction
+
+Anode provides three components that work together to provide a global,
+secure, and mobile addressing system for computer networks:
+
+1) An addressing system based on public key cryptography enabling network
+ devices or applications to assign themselves secure, unique, and globally
+ reachable network addresses in a flat address space.
+
+2) A system enabling network participants holding global addresses to locate
+ one another on local or global networks with "zero configuration."
+
+3) A communications protocol for communication between addressed network
+ participants that requires no special operating system support and no
+ changes to existing network infrastructure.
+
+Using Anode, both fixed and mobile applications and devices can communicate
+directly as if they were all connected to the same VPN. Anode restores the
+original vision of the Internet as a "flat" network where anything can talk
+to anything, and adds the added benefits of address mobility and strong
+protection against address spoofing and other protocol level attacks.
+
+1.1. Design Philosophy
+
+Anode's design philosophy is the classical "KISS" principle: "Keep It Simple
+Stupid." Anode's design principles are:
+
+#1: Do not try to solve too many problems at once, and stay in scope.
+
+Anode does not attempt to solve too many problems at once. It attempts to
+solve the problems of mobile addressing, address portability, and "flat"
+addressing in the presence of NAT or other barriers.
+
+It does not attempt to duplicate the full functionality of SSL, X.509, SSH,
+XMPP, an enterprise service bus, a pub/sub architecture, BitTorrent, etc. All
+of those protocols and services can be used over Anode if their functionality
+is desired.
+
+#2: Avoid state management.
+
+State multiplies the complexity and failure modes of network protocols. State
+also tends to get in the way of the achievement of new features implicitly
+(see principle #4). Avoid state whenever possible.
+
+#3: Avoid algorithm and dependency bloat.
+
+Anode uses only elliptic curve Diffie-Hellman (EC-DH) and AES-256. No other
+cryptographic algorithms or hash functions are presently necessary. This
+yields implementations compact enough for embedded devices.
+
+Anode also requires few or no dependencies, depending on whether the two
+needed cryptographic algorithms are obtained through a library or included.
+No other protocols or libraries are required in an implementation.
+
+#4: Achieve features implicitly.
+
+Use a simple stateless design that allows features to be achieved implicitly
+rather than specified explicitly. For example, Anode can do multi-homing and
+could be used to build a mesh network, but neither of these features is
+explicitly specified.
+
+*****************************************************************************
+
+2. Core Concepts and Algorithms
+
+This section describes addresses, zones, common algorithms, and other core
+concepts.
+
+2.1. Zones
+
+A zone is a 32-bit integer encoded into every Anode address. Zones serve to
+assist in the location of peers by address on global IP networks. They are
+not presently significant for local communications, though they could be
+used to partition addresses into groups or link them with configuration
+options.
+
+Each zone has a corresponding zone file which can be fetched in a number of
+ways (see below). A zone file is a flat text format dictionary of the format
+"key=value" separated by carriage returns. Line feeds are ignored, and any
+character may be escaped with a backslash (\) character. Blank lines are
+ignored.
+
+The following entries must appear in a zone file:
+
+n=<zone name>
+d=<zone description>
+c=<zone contact, e-mail address of zone administrator>
+r=<zone revision, monotonically increasing integer with each edit>
+ttl=<seconds before zone file should be re-checked for changes>
+
+Additional fields may appear as well, including fields specific to special
+applications or protocols supported within the zone. Some of these are
+defined in this document.
+
+Zone file fetching mechanisms are described below. Multiple mechanisms are
+specified to enable fallback in the event that one mechanism is not available.
+
+2.1.1. Zone File Retrieval
+
+Zone files are retrieved via HTTP, with the HTTP address being formed in one
+of two ways.
+
+The preferred DNS method:
+
+To fetch a zone file via DNS, use the zone ID to generate a host name and URI
+of the form:
+
+ http://a--XXXXXXXX.net/z
+
+The XXXXXXXX field is the zone ID in hexadecimal.
+
+The fallback IP method:
+
+For fallback in the absence of DNS, the zone ID can be used directly as an
+IPv4 or IPv4-mapped-to-IPv6 IP address. A URI is generated of the form:
+
+ http://ip_address/z
+
+Support for this method requires that a zone ID be chosen to correspond to a
+permanent IPv4 (preferably mappable to IPv6 space as well) IP address.
+
+2.1.2. Zone ID Reservation
+
+By convention, a zone ID is considered reserved when a domain of the form
+"a--XXXXXXXX.net" (where XXXXXXXX is the ID in hex) is registered.
+
+It is recommended that this be done even for zone IDs not used for global
+address location in order to globally reserve them.
+
+2.2. Addresses
+
+Anode addresses are binary strings containing a 32-bit zone ID, a public key,
+and possibly other fields. Only one address type is presently defined:
+
+|---------------------------------------------------------------------------|
+| Name | Type ID | Elliptic Curve Parameters | Total Length |
+|---------------------------------------------------------------------------|
+| ANODE-256-40 | 1 | NIST-P-256 | 40 |
+|---------------------------------------------------------------------------|
+
+|---------------------------------------------------------------------------|
+| Name | Binary Layout |
+|---------------------------------------------------------------------------|
+| ANODE-256-40 | <type[1]><zone[4]><unused[2]><public key[33]> |
+|---------------------------------------------------------------------------|
+
+The public key is a "compressed" form elliptic curve public key as described
+in RFC5480.
+
+The unused section of the address must be zero. These bytes are reserved for
+future use.
+
+2.2.1. ASCII Format For Addresses
+
+Addresses are encoded in ASCII using base-32, which provides a quotable and
+printable encoding that is of manageable length and is case-insensitive. For
+example, an ANODE-256-40 address is 64 characters long in base-32 encoding.
+
+2.3. Relaying
+
+An Anode peer may optionally relay packets to any other reachable peer.
+Relaying is accomplished by sending a packet to a peer with the recipient set
+to the final recipient. The receiving peer will, if relaying is allowed and if
+it knows of or can reach the recipient, forward the packet.
+
+No error is returned if relaying fails, so relay paths are treated as possible
+paths for communication until a return is received in the same way as direct
+paths.
+
+Relaying can be used by peers to send messages indirectly, locate one
+another, and determine network location information to facilitate the
+establishment of direct communications.
+
+Peers may refuse to relay or may limit the transmission rate at which packets
+can be relayed.
+
+2.3.1. Zone Relays
+
+If a zone's addresses are globally reachable on global IP networks, it must
+have one or more zone relays. These must have globally reachable public
+static IP addresses.
+
+Zone relays are specified in the zone file in the following format:
+
+ zr.<address checksum>=<ip>[,<ip>]:<udp port>:<tcp port>:<anode addresses>
+
+The address checksum is the sum of the bytes in the Anode address modulus
+the number of "zr" entries, in hexadecimal. For example, if a zone had four
+global relays its zone file could contain the lines:
+
+ zr.0=1.2.3.4:4343:4344:klj4j3...
+ zr.1=2.3.4.5:4343:4344:00194j...
+ zr.2=3.4.5.6:4343:4344:1j42zz...
+ zr.3=4.5.6.7:4343:4344:z94j1q...
+
+The relay would be chosen by taking the sum of the bytes in the address
+modulo 4. For example, if the bytes of an address sum to 5081 then relay
+zr.1 would be used to communicate with that address.
+
+If more than one IP address is listed for a given relay, the peer must choose
+at random from among the addresses of the desired type (IPv4 or IPv6).
+
+Each relay must have one Anode address for every address type supported within
+the zone. (At present there is only one address type defined.)
+
+Peers should prefer UDP and fall back to TCP only if UDP is not available.
+
+To make itself available, a peer must make itself known to its designated zone
+relay. This is accomplished by sending a PING message.
+
+2.4. Key Agreement and Derivation
+
+Key agreement is performed using elliptic curve Diffie-Hellman. This yields
+a raw key whose size depends on the elliptic curve parameters in use.
+
+The following algorithm is used to derive a key of any length from a raw
+key generated through key agreement:
+
+1) Zero the derived key buffer.
+2) Determine the largest of the original raw key or the derived key.
+3) Loop from 0 to the largest length determined in step 2, XOR each byte of
+ the derived key buffer with the corresponding byte of the original key
+ buffer with each index being modulus the length of the respective buffer.
+
+2.5. Message Authentication
+
+For message authentication, CMAC-AES (with AES-256) is used. This is also
+known in some literature as OMAC1-AES. The key is derived from key agreement
+between the key pair of the sending peer and the address of the recipient.
+
+2.6. AES-DIGEST
+
+To maintain cryptographic algorithm frugality, a cryptographic hash function
+is constructed from the AES-256 cipher. This hash function uses the common
+Davis-Meyer construction with Merkle-Damgård length padding.
+
+It is described by the following pseudocode:
+
+ byte previous_digest[16]
+ byte digest[16] = { 0,0,... }
+ byte block[32] = { 0,0,... }
+ integer block_counter = 0
+
+ ; digest message
+ for each byte b of message
+ block[block_counter] = b
+ block_counter = block_counter + 1
+ if block_counter == 32 then
+ block_counter = 0
+ save digest[] in previous_digest[]
+ encrypt digest[] with aes-256 using block[] as 256-bit aes-256 key
+ xor digest[] with previous_digest[]
+ end if
+ next
+
+ ; append end marker, do final block
+ block[block_counter] = 0x80
+ block_counter = block_counter + 1
+ zero rest of block[] from block_counter to 15
+ save digest[] in previous_digest[]
+ encrypt digest[] with aes-256 using block[] as 256-bit aes-256 key
+ xor digest[] with previous_digest[]
+
+ ; Merkle-Damgård length padding
+ zero first 8 bytes of block[]
+ fill last 8 bytes of block[] w/64-bit length in big-endian order
+ save digest[] in previous_digest[]
+ encrypt digest[] with aes-256 using block[] as 256-bit aes-128 key
+ xor digest[] with previous_digest[]
+
+ ; digest[] now contains 128-bit message digest
+
+2.7. Short Address Identifiers (Address IDs)
+
+A short 8-byte version of the Anode address is used in the protocol to reduce
+transmission overhead when both sides are already aware of the other's full
+address.
+
+The short address identifier is formed by computing the AES-DIGEST of the
+full address and then XORing the first 8 bytes of the digest with the last
+8 bytes to yield an 8-byte shortened digest.
+
+2.8. DNS Resolution of Anode Addresses
+
+Anode addresses can be saved in DNS TXT records in the following format:
+
+anode:<address in base32 ASCII encoding>
+
+This permits Anode addresses to be resolved from normal DNS host name.
+
+2.9. Packet Transmission Mechanisms
+
+2.9.1. UDP Transmission
+
+The recommended method of sending Anode packets is UDP. Each packet is simply
+sent as a UDP packet.
+
+2.9.2. TCP Transmission
+
+To send packets over TCP, each packet is prefixed by its size as a 16-bit
+integer.
+
+2.9.3. HTTP Transmission
+
+Anode packets may be submitted in HTTP POST transactions for transport over
+networks where HTTP is the only available protocol.
+
+Anode packets are simply prefixed with a 16-byte packet size and concatenated
+together just as they are in a TCP stream. One or more packets may be sent
+with each HTTP POST transaction for improved performance.
+
+Since this method is intended for use in "hostile" or highly restricted
+circumstances, no additional details such as special headers or MIME types
+are specified to allow maximum flexibility. Peers should ignore anything
+other than the payload.
+
+2.10. Endpoints
+
+An endpoint indicates a place where Anode packets may be sent. The following
+endpoint types are specified:
+
+|---------------------------------------------------------------------------|
+| Endpoint Type | Description | Address Format |
+|---------------------------------------------------------------------------|
+| 0x00 | Unspecified | (none) |
+| 0x01 | Ethernet | <mac[6]> |
+| 0x02 | UDP/IPv4 | <ip[4]><port[2]> |
+| 0x03 | TCP/IPv4 | <ip[4]><port[2]> |
+| 0x04 | UDP/IPv6 | <ip[16]><port[2]> |
+| 0x05 | TCP/IPv6 | <ip[16]><port[2]> |
+| 0x06 | HTTP | <null-terminated full URI> |
+|---------------------------------------------------------------------------|
+
+Endpoints are encoded by beginning with a single byte indicating the endpoint
+type followed by the address information required for the given type.
+
+Note that IP ports bear no relationship to Anode protocol ports.
+
+2.11. Notes
+
+All integers in the protocol are transmitted in network (big endian) byte
+order.
+
+*****************************************************************************
+
+3. Common Packet Format
+
+A common header is used for all Anode packets:
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Hop Count | 1 | 8-bit hop count (not included in MAC) |
+| Flags | 1 | 8-bit flags |
+| MAC | 8 | 8 byte shortened CMAC-AES of packet |
+| Sender Address | ? | Full address or short ID of sender |
+| Recipient Address | ? | Full address or short ID of recipient |
+| Peer IDs | 1 | Two 4-bit peer IDs: sender, recipient |
+| Message Type | 1 | 8-bit message type |
+| Message | ? | Message payload |
+|---------------------------------------------------------------------------|
+
+3.1. Hop Count
+
+The hop count begins at zero and must be incremented by each peer that relays
+the packet to another peer. The hop count must not wrap to zero at 255.
+
+Because the hop count is modified in transit, it is not included in MAC
+calculation or authentication.
+
+The hop count is used to prioritize endpoints that are direct over endpoints
+that involve relaying, or to prioritize closer routes over more distant
+ones.
+
+3.2. Flags and Flag Behavior
+
+|---------------------------------------------------------------------------|
+| Flag | Description |
+|---------------------------------------------------------------------------|
+| 0x01 | Sender address fully specified |
+| 0x02 | Recipient address fully specified |
+| 0x04 | Authentication error response |
+|---------------------------------------------------------------------------|
+
+If flag 0x01 is set, then the sender address will be the full address rather
+than a short address identifier. The length of the address can be determined
+from the first byte of the address, which always specifies the address type.
+Flag 0x02 has the same meaning for the recipient address.
+
+A peer must send fully specified sender addresses until it receives a response
+from the recipient. At this point the sender may assume that the recipient
+knows its address and use short a short sender address instead. This
+assumption should time out, with a recommended timeout of 60 seconds.
+
+There is presently no need to send fully specified recipient addresses, but
+the flag is present in case it is needed and must be honored.
+
+Flag 0x04 indicates that this is an error response containing a failed
+authentication error. Since authentication failed, this packet may not have
+a valid MAC. Packets with this flag must never have any effect other than
+to inform of an error. This error, since it is unauthenticated, must never
+have any side effects such as terminating a connection.
+
+3.3. MAC
+
+The MAC is calculated as follows:
+
+1) Temporarily set the 64-bit/8-byte MAC field in the packet to the packet's
+ size as a 64-bit big-endian integer.
+2) Calculate the MAC for the entire packet (excluding the first byte) using
+ the key agreed upon between the sender and the recipient, resulting in a
+ 16 byte full CMAC-AES MAC.
+3) Derive the 8 byte packet MAC by XORing the first 8 bytes of the full 16
+ byte CMAC-AES MAC with the last 8 bytes. Place this into the packet's MAC
+ field.
+
+3.4. Peer IDs
+
+Peer IDs provide a method for up to 15 different peers to share an address,
+each with a unique ID allowing packets to be routed to them individually.
+
+A peer ID of zero indicates "any" or "unspecified." Real peers must have a
+nonzero peer ID. In the normal single peer per address case, any peer ID may
+be used. If multiple peers are to share an address, some implementation-
+dependent method must be used to ensure that each peer has a unique peer ID.
+
+Relaying peers must follow these rules based on the recipient peer ID when
+relaying messages:
+
+ - IF the peer ID is zero or if the peer ID is not known, the message must
+ be forwarded to a random endpoint for the given recipient address.
+ - IF the peer ID is nonzero and matches one or more known endpoints for the
+ given recipient address and peer ID, the message must only be sent to
+ a matching endpoint.
+
+A receiving peer should process any message that it receives regardless of
+whether its recipient peer ID is correct. The peer ID is primarily for relays.
+
+Peers should typically send messages with a nonzero recipient peer ID when
+responding to or involved in a conversation with a specific peer (e.g. a
+streaming connection), and send zero recipient peer IDs otherwise.
+
+3.5. Short Address Conflict Disambiguation
+
+In the unlikely event of two Anode addresses with the same short identifier,
+the recipient should use MAC validation to disambiguate. The peer ID must not
+be relied upon for this purpose.
+
+*****************************************************************************
+
+4. Basic Signaling and Transport Protocol
+
+4.1. Message Types
+
+|---------------------------------------------------------------------------|
+| Type | ID | Description |
+|---------------------------------------------------------------------------|
+| ERROR | 0x00 | Error response |
+| PING | 0x01 | Echo request |
+| PONG | 0x02 | Echo response |
+| EPC_REQ | 0x03 | Endpoint check request |
+| EPC | 0x04 | Endpoint check response |
+| EPI | 0x05 | Endpoint information |
+| NAT_T | 0x06 | NAT traversal message |
+| NETID_REQ | 0x07 | Request network address identification and/or test |
+| NETID | 0x08 | Response to network address identification request |
+| DGRAM | 0x09 | Simple UDP-like datagram |
+|---------------------------------------------------------------------------|
+
+4.2. Message Details
+
+4.2.1. ERROR
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Error Code | 2 | 16-bit error code |
+| Error Arguments | ? | Error arguments, depending on error type |
+|---------------------------------------------------------------------------|
+
+Error arguments are empty unless otherwise stated below.
+
+Error codes:
+
+|---------------------------------------------------------------------------|
+| Error Code | Description |
+|---------------------------------------------------------------------------|
+| 0x01 | Message not valid |
+| 0x02 | Message authentication or decryption failed |
+| 0x03 | Relaying and related features not authorized |
+| 0x04 | Relay recipient not reachable |
+|---------------------------------------------------------------------------|
+
+Generation of errors is optional. A peer may choose to ignore invalid
+messages or to throttle the sending of errors.
+
+4.2.2. PING
+
+(Payload unspecified.)
+
+Request echo of payload as PONG message.
+
+4.2.3. PONG
+
+(Payload unspecified.)
+
+Echoed payload of received PING message.
+
+4.2.4. EPC_REQ
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Request ID | 4 | 32-bit request ID |
+|---------------------------------------------------------------------------|
+
+Request echo of request ID in EPC message, used to check and learn endpoints.
+
+To learn a network endpoint for a peer, CHECK_REQ is sent. If CHECK is
+returned with a valid request ID, the endpoint is considered valid.
+
+4.2.5. EPC
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Request ID | 4 | 32-bit request ID echoed back |
+|---------------------------------------------------------------------------|
+
+Response to EPC_REQ containing request ID.
+
+4.2.6. EPI
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Flags | 1 | 8-bit flags |
+| Endpoint | ? | Endpoint type and address |
+| NAT-T mode | 1 | 8-bit NAT traversal mode |
+| NAT-T options | ? | Options related to specified NAT-T mode |
+|---------------------------------------------------------------------------|
+
+EPI stands for EndPoint Identification, and is sent to notify another peer of
+a network endpoint where the sending peer is reachable.
+
+If the receiving peer is interested in communicating with the sending peer,
+the receiving peer must send EPC_REQ to the sending peer at the specified
+endpoint to check the validity of that endpoint. The endpoint is learned if a
+valid EPC is returned.
+
+If the endpoint in EPI is unspecified, the actual source of the EPI message
+is the endpoint. This allows EPI messages to be broadcast on a local LAN
+segment to advertise the presence of an address on a local network. EPI
+broadcasts on local IP networks must be made to UDP port 8737.
+
+Usually EPI is sent via relays (usually zone relays) to inform a peer of an
+endpoint for direct communication.
+
+There are presently no flags, so flags must be zero.
+
+4.2.7. NAT_T
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| NAT-T mode | 1 | 8-bit NAT traversal mode |
+| NAT-T options | ? | Options related to specified NAT-T mode |
+|---------------------------------------------------------------------------|
+
+NAT_T is used to send messages specific to certain NAT traversal modes.
+
+4.2.8. NETID_REQ
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Request ID | 4 | 32-bit request ID |
+| Endpoint | ? | Endpoint type and address information |
+|---------------------------------------------------------------------------|
+
+When a NETID_REQ message is received, the recipient attempts to echo it back
+as a NETID message to the specified endpoint address. If the endpoint is
+unspecified, the recipient must fill it in with the actual origin of the
+NETID_REQ message. This allows a peer to cooperate with another peer (usually
+a zone relay) to empirically determine its externally visible network
+address information.
+
+A peer may ignore NETID_REQ or respond with an error if it does not allow
+relaying.
+
+4.2.9. NETID
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Request ID | 4 | 32-bit request ID echoed back |
+| Endpoint Type | 1 | 8-bit endpoint type |
+| Endpoint Address | ? | Endpoint Address (size depends on type) |
+|---------------------------------------------------------------------------|
+
+NETID is sent in response to NETID_REQ to the specified endpoint address. It
+always contains the endpoint address to which it was sent.
+
+4.2.10. DGRAM
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Source Port | 2 | 16-bit source port |
+| Destination Port | 2 | 16-bit destination port |
+| Payload | ? | Datagram packet payload |
+|---------------------------------------------------------------------------|
+
+A datagram is a UDP-like message without flow control or delivery assurance.
+
+*****************************************************************************
+
+5. Stream Protocol
+
+The stream protocol is very similar to TCP, though it omits some features
+that are not required since they are taken care of by the encapsulating
+protocol. SCTP was also an inspiration in the design.
+
+5.1. Message Types
+
+|---------------------------------------------------------------------------|
+| Type | ID | Description |
+|---------------------------------------------------------------------------|
+| S_OPEN | 20 | Initiate a streaming connection (like TCP SYN) |
+| S_CLOSE | 21 | Terminate a streaming connection (like TCP RST/FIN) |
+| S_DATA | 22 | Data packet |
+| S_ACK | 23 | Acknowedge receipt of one or more data packets |
+| S_DACK | 24 | Combination of DATA and ACK |
+|---------------------------------------------------------------------------|
+
+5.2. Message Details
+
+5.2.1. S_OPEN
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Sender Link ID | 2 | 16-bit sender link ID |
+| Destination Port | 2 | 16-bit destination port |
+| Window Size | 2 | 16-bit window size in 1024-byte increments |
+| Init. Seq. Number | 4 | 32-bit initial sequence number |
+| Flags | 1 | 8-bit flags |
+|---------------------------------------------------------------------------|
+
+The OPEN message corresponds to TCP SYN, and initiates a connection. It
+specifies the initial window size for the sender and the sender's initial
+sequence number, which should be randomly chosen to prevent replay attacks.
+
+If OPEN is successful, the recipient sends its own OPEN to establish the
+connetion. If OPEN is unsuccessful, CLOSE is sent with its initial and current
+sequence numbers equal and an appropriate reason such as "connection refused."
+
+The sender link ID must be unique for a given recipient.
+
+If flag 01 is set, the sender link ID is actually a source port where the
+sender might be listening for connections as well. This exactly duplicates
+the behavior of standard TCP. Otherwise, the sender link ID is simply an
+arbitrary number that the sender uses to identify the connection with this
+recipient and there is no port of origin. Ports of origin are optional for
+Anode streaming connections to permit greater scalability.
+
+5.2.2. S_CLOSE
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Sender Link ID | 2 | 16-bit sender link ID |
+| Destination Port | 2 | 16-bit destination port |
+| Flags | 1 | 8-bit flags |
+| Reason | 1 | 8-bit close reason |
+| Init. Seq. Number | 4 | 32-bit initial sequence number |
+| Sequence Number | 4 | 32-bit current sequence number |
+|---------------------------------------------------------------------------|
+
+The CLOSE message serves a function similar to TCP FIN. The initial sequence
+number is the original starting sequence number sent with S_OPEN, while the
+current sequence number is the sequence number corresponding to the close
+and must be ACKed to complete the close operation. The use of the initial
+sequence number helps to serve as a key to prevent replay attacks.
+
+CLOSE is also used to indicate a failed OPEN attempt. In this case the current
+sequence number will be equal to the initial sequence number and no ACK will
+be expected.
+
+There are currently no flags, so flags must be zero.
+
+The reason field describes the reason for the close:
+
+|---------------------------------------------------------------------------|
+| Reason Code | Description |
+|---------------------------------------------------------------------------|
+| 00 | Application closed connection |
+| 01 | Connection refused |
+| 02 | Protocol error |
+| 03 | Timed out |
+|---------------------------------------------------------------------------|
+
+Established connections will usually be closed with reason 00, while reason
+01 is usually provided if an OPEN is received but the port is not bound.
+
+5.2.3. S_DATA
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Sender Link ID | 2 | 16-bit sender link ID |
+| Destination Port | 2 | 16-bit destination port |
+| Sequence Number | 4 | 32-bit sequence number |
+| Payload | ? | Data payload |
+|---------------------------------------------------------------------------|
+
+The DATA message carries a packet of data, with the sequence number
+determining order. The sequence number is monotonically incremented with
+each data packet, and wraps at the maximum value of an unsigned 32-bit
+integer.
+
+5.2.4. S_ACK
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Sender Link ID | 2 | 16-bit sender link ID |
+| Destination Port | 2 | 16-bit destination port |
+| Window Size | 2 | 16-bit window size in 1024-byte increments |
+| Acknowledgements | ? | One or more acknowledgements (see below) |
+|---------------------------------------------------------------------------|
+
+Each acknowledgement is a 32-bit integer followed by an 8-bit integer (5 bytes
+total). The 32-bit integer is the first sequence number to acknowledge, and
+the 8-bit integer is the number of sequential following sequence numbers to
+acknowledge. For example "1, 4" would acknowledge sequence numbers 1, 2, 3,
+and 4.
+
+5.2.5. S_DACK
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Sender Link ID | 2 | 16-bit sender link ID |
+| Destination Port | 2 | 16-bit destination port |
+| Window Size | 2 | 16-bit window size in 1024-byte increments |
+| Num. Acks | 1 | 8-bit number of acknowledgements |
+| Acknowledgements | ? | One or more acknowledgements |
+| Payload | ? | Data payload |
+|---------------------------------------------------------------------------|
+
+The DACK message combines ACK and DATA, allowing two peers that are both
+transmitting data to efficiently ACK without a separate packet.
diff --git a/attic/historic/anode/libanode/Makefile b/attic/historic/anode/libanode/Makefile
new file mode 100644
index 00000000..088587bd
--- /dev/null
+++ b/attic/historic/anode/libanode/Makefile
@@ -0,0 +1,33 @@
+SYSNAME:=${shell uname}
+SYSNAME!=uname
+include ../config.mk.${SYSNAME}
+
+LIBANODE_OBJS= \
+ impl/aes.o \
+ impl/dictionary.o \
+ impl/dns_txt.o \
+ impl/ec.o \
+ impl/environment.o \
+ impl/misc.o \
+ impl/thread.o \
+ address.o \
+ aes_digest.o \
+ errors.o \
+ identity.o \
+ network_address.o \
+ secure_random.o \
+ system_transport.o \
+ uri.o
+# zone.o
+
+all: $(LIBANODE_OBJS)
+ ar rcs libanode.a $(LIBANODE_OBJS)
+ ranlib libanode.a
+ $(CC) $(CFLAGS) -o utils/anode-make-identity utils/anode-make-identity.c $(LIBANODE_OBJS) $(LIBANODE_LIBS)
+
+clean: force
+ rm -f $(LIBANODE_OBJS)
+ rm -f libanode.$(DLLEXT) libanode.a
+ rm -f utils/anode-make-identity
+
+force: ;
diff --git a/attic/historic/anode/libanode/address.c b/attic/historic/anode/libanode/address.c
new file mode 100644
index 00000000..e7ab7837
--- /dev/null
+++ b/attic/historic/anode/libanode/address.c
@@ -0,0 +1,98 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "impl/aes.h"
+#include "impl/ec.h"
+#include "impl/misc.h"
+#include "impl/types.h"
+#include "anode.h"
+
+int AnodeAddress_calc_short_id(
+ const AnodeAddress *address,
+ AnodeAddressId *short_address_id)
+{
+ unsigned char digest[16];
+
+ switch(AnodeAddress_get_type(address)) {
+ case ANODE_ADDRESS_ANODE_256_40:
+ Anode_aes_digest(address->bits,ANODE_ADDRESS_LENGTH_ANODE_256_40,digest);
+ break;
+ default:
+ return ANODE_ERR_ADDRESS_INVALID;
+ }
+
+ *((uint64_t *)short_address_id->bits) = ((uint64_t *)digest)[0] ^ ((uint64_t *)digest)[1];
+
+ return 0;
+}
+
+int AnodeAddress_get_zone(const AnodeAddress *address,AnodeZone *zone)
+{
+ switch(AnodeAddress_get_type(address)) {
+ case ANODE_ADDRESS_ANODE_256_40:
+ *((uint32_t *)&(zone->bits[0])) = *((uint32_t *)&(address->bits[1]));
+ return 0;
+ }
+ return ANODE_ERR_ADDRESS_INVALID;
+}
+
+int AnodeAddress_to_string(const AnodeAddress *address,char *buf,int len)
+{
+ const unsigned char *inptr;
+ char *outptr;
+ unsigned int i;
+
+ switch(AnodeAddress_get_type(address)) {
+ case ANODE_ADDRESS_ANODE_256_40:
+ if (len < (((ANODE_ADDRESS_LENGTH_ANODE_256_40 / 5) * 8) + 1))
+ return ANODE_ERR_BUFFER_TOO_SMALL;
+ inptr = (const unsigned char *)address->bits;
+ outptr = buf;
+ for(i=0;i<(ANODE_ADDRESS_LENGTH_ANODE_256_40 / 5);++i) {
+ Anode_base32_5_to_8(inptr,outptr);
+ inptr += 5;
+ outptr += 8;
+ }
+ *outptr = (char)0;
+ return ((ANODE_ADDRESS_LENGTH_ANODE_256_40 / 5) * 8);
+ }
+ return ANODE_ERR_ADDRESS_INVALID;
+}
+
+int AnodeAddress_from_string(const char *str,AnodeAddress *address)
+{
+ const char *blk_start = str;
+ const char *ptr = str;
+ unsigned int address_len = 0;
+
+ while (*ptr) {
+ if ((unsigned long)(ptr - blk_start) == 8) {
+ if ((address_len + 5) > sizeof(address->bits))
+ return ANODE_ERR_ADDRESS_INVALID;
+ Anode_base32_8_to_5(blk_start,(unsigned char *)&(address->bits[address_len]));
+ address_len += 5;
+ blk_start = ptr;
+ }
+ ++ptr;
+ }
+
+ if (ptr != blk_start)
+ return ANODE_ERR_ADDRESS_INVALID;
+ if (AnodeAddress_get_type(address) != ANODE_ADDRESS_ANODE_256_40)
+ return ANODE_ERR_ADDRESS_INVALID;
+
+ return 0;
+}
diff --git a/attic/historic/anode/libanode/aes_digest.c b/attic/historic/anode/libanode/aes_digest.c
new file mode 100644
index 00000000..07b0fc7a
--- /dev/null
+++ b/attic/historic/anode/libanode/aes_digest.c
@@ -0,0 +1,85 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "anode.h"
+#include "impl/aes.h"
+#include "impl/misc.h"
+#include "impl/types.h"
+
+void Anode_aes_digest(const void *const message,unsigned long message_len,void *const hash)
+{
+ unsigned char previous_digest[16];
+ unsigned char digest[16];
+ unsigned char block[32];
+ const unsigned char *in = (const unsigned char *)message;
+ const unsigned char *end = in + message_len;
+ unsigned long block_counter;
+ AnodeAesExpandedKey expkey;
+
+ ((uint64_t *)digest)[0] = 0ULL;
+ ((uint64_t *)digest)[1] = 0ULL;
+ ((uint64_t *)block)[0] = 0ULL;
+ ((uint64_t *)block)[1] = 0ULL;
+ ((uint64_t *)block)[2] = 0ULL;
+ ((uint64_t *)block)[3] = 0ULL;
+
+ /* Davis-Meyer hash function built from block cipher */
+ block_counter = 0;
+ while (in != end) {
+ block[block_counter++] = *(in++);
+ if (block_counter == 32) {
+ block_counter = 0;
+ ((uint64_t *)previous_digest)[0] = ((uint64_t *)digest)[0];
+ ((uint64_t *)previous_digest)[1] = ((uint64_t *)digest)[1];
+ Anode_aes256_expand_key(block,&expkey);
+ Anode_aes256_encrypt(&expkey,digest,digest);
+ ((uint64_t *)digest)[0] ^= ((uint64_t *)previous_digest)[0];
+ ((uint64_t *)digest)[1] ^= ((uint64_t *)previous_digest)[1];
+ }
+ }
+
+ /* Davis-Meyer end marker */
+ block[block_counter++] = 0x80;
+ while (block_counter != 32) block[block_counter++] = 0;
+ ((uint64_t *)previous_digest)[0] = ((uint64_t *)digest)[0];
+ ((uint64_t *)previous_digest)[1] = ((uint64_t *)digest)[1];
+ Anode_aes256_expand_key(block,&expkey);
+ Anode_aes256_encrypt(&expkey,digest,digest);
+ ((uint64_t *)digest)[0] ^= ((uint64_t *)previous_digest)[0];
+ ((uint64_t *)digest)[1] ^= ((uint64_t *)previous_digest)[1];
+
+ /* Merkle-Damgård length padding */
+ ((uint64_t *)block)[0] = 0ULL;
+ if (sizeof(message_len) >= 8) { /* 32/64 bit? this will get optimized out */
+ block[8] = (uint8_t)((uint64_t)message_len >> 56);
+ block[9] = (uint8_t)((uint64_t)message_len >> 48);
+ block[10] = (uint8_t)((uint64_t)message_len >> 40);
+ block[11] = (uint8_t)((uint64_t)message_len >> 32);
+ } else ((uint32_t *)block)[2] = 0;
+ block[12] = (uint8_t)(message_len >> 24);
+ block[13] = (uint8_t)(message_len >> 16);
+ block[14] = (uint8_t)(message_len >> 8);
+ block[15] = (uint8_t)message_len;
+ ((uint64_t *)previous_digest)[0] = ((uint64_t *)digest)[0];
+ ((uint64_t *)previous_digest)[1] = ((uint64_t *)digest)[1];
+ Anode_aes256_expand_key(block,&expkey);
+ Anode_aes256_encrypt(&expkey,digest,digest);
+ ((uint64_t *)digest)[0] ^= ((uint64_t *)previous_digest)[0];
+ ((uint64_t *)digest)[1] ^= ((uint64_t *)previous_digest)[1];
+
+ ((uint64_t *)hash)[0] = ((uint64_t *)digest)[0];
+ ((uint64_t *)hash)[1] = ((uint64_t *)digest)[1];
+}
diff --git a/attic/historic/anode/libanode/anode.h b/attic/historic/anode/libanode/anode.h
new file mode 100644
index 00000000..e0c51e2e
--- /dev/null
+++ b/attic/historic/anode/libanode/anode.h
@@ -0,0 +1,795 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _ANODE_ANODE_H
+#define _ANODE_ANODE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+#define ANODE_ADDRESS_LENGTH_ANODE_256_40 40
+#define ANODE_ADDRESS_MAX_LENGTH 40
+#define ANODE_ADDRESS_SECRET_LENGTH_ANODE_256_40 32
+#define ANODE_ADDRESS_MAX_SECRET_LENGTH 32
+
+#define ANODE_ADDRESS_ID_LENGTH 8
+#define ANODE_ZONE_LENGTH 4
+
+#define ANODE_ERR_NONE 0
+#define ANODE_ERR_INVALID_ARGUMENT (-10000)
+#define ANODE_ERR_OUT_OF_MEMORY (-10001)
+#define ANODE_ERR_INVALID_URI (-10002)
+#define ANODE_ERR_BUFFER_TOO_SMALL (-10003)
+#define ANODE_ERR_ADDRESS_INVALID (-10010)
+#define ANODE_ERR_ADDRESS_TYPE_NOT_SUPPORTED (-10011)
+#define ANODE_ERR_CONNECTION_CLOSED (-10012)
+#define ANODE_ERR_CONNECTION_CLOSED_BY_REMOTE (-10013)
+#define ANODE_ERR_CONNECT_FAILED (-10014)
+#define ANODE_ERR_UNABLE_TO_BIND (-10015)
+#define ANODE_ERR_TOO_MANY_OPEN_SOCKETS (-10016)
+#define ANODE_ERR_DNS_NAME_NOT_FOUND_OR_TIMED_OUT (-10017)
+
+/**
+ * Get a human-readable error description for an error code
+ *
+ * The value of 'err' can be either negative or positive.
+ *
+ * @param err Error code
+ * @return Human-readable description
+ */
+extern const char *Anode_strerror(int err);
+
+/* ----------------------------------------------------------------------- */
+/* Secure random source */
+/* ----------------------------------------------------------------------- */
+
+/**
+ * Opaque secure random instance
+ */
+typedef void AnodeSecureRandom;
+
+/**
+ * Initialize a secure random source
+ *
+ * No cleanup/destructor is necessary.
+ *
+ * @param srng Random structure to initialize
+ */
+extern AnodeSecureRandom *AnodeSecureRandom_new();
+
+/**
+ * Generate random bytes
+ *
+ * @param srng Secure random source
+ * @param buf Buffer to fill
+ * @param count Number of bytes to generate
+ */
+extern void AnodeSecureRandom_gen_bytes(AnodeSecureRandom *srng,void *buf,long count);
+
+/**
+ * Destroy and free a secure random instance
+ *
+ * @param srng Secure random source
+ */
+extern void AnodeSecureRandom_delete(AnodeSecureRandom *srng);
+
+/* ----------------------------------------------------------------------- */
+/* AES-256 derived Davis-Meyer hash function */
+/* ----------------------------------------------------------------------- */
+
+/**
+ * Digest a message using AES-DIGEST to yield a 16-byte hash code
+ *
+ * @param message Message to digest
+ * @param message_len Length of message in bytes
+ * @param hash Buffer to store 16 byte hash code
+ */
+extern void Anode_aes_digest(
+ const void *const message,
+ unsigned long message_len,
+ void *const hash);
+
+/* ----------------------------------------------------------------------- */
+/* Address Types and Components */
+/* ----------------------------------------------------------------------- */
+
+/**
+ * Anode address
+ *
+ * The first byte always identifies the address type, which right now can
+ * only be type 1 (ANODE-256-40).
+ */
+typedef struct
+{
+ char bits[ANODE_ADDRESS_MAX_LENGTH];
+} AnodeAddress;
+
+/**
+ * 8-byte short Anode address ID
+ */
+typedef struct
+{
+ char bits[ANODE_ADDRESS_ID_LENGTH];
+} AnodeAddressId;
+
+/**
+ * 4-byte Anode zone ID
+ */
+typedef struct
+{
+ char bits[ANODE_ZONE_LENGTH];
+} AnodeZone;
+
+/**
+ * Anode address types
+ */
+enum AnodeAddressType
+{
+ ANODE_ADDRESS_ANODE_256_40 = 1
+};
+
+/**
+ * Get the type of an Anode address
+ *
+ * This is a shortcut macro for just looking at the first byte and casting
+ * it to the AnodeAddressType enum.
+ *
+ * @param a Pointer to address
+ * @return Type as enum AnodeAddressType
+ */
+#define AnodeAddress_get_type(a) ((enum AnodeAddressType)((a)->bits[0]))
+
+/**
+ * Calculate the short 8 byte address ID from an address
+ *
+ * @param address Binary address
+ * @param short_address_id Buffer to store 8-byte short address ID
+ * @return 0 on success or error code on failure
+ */
+extern int AnodeAddress_calc_short_id(
+ const AnodeAddress *address,
+ AnodeAddressId *short_address_id);
+
+/**
+ * Extract the zone from an anode address
+ *
+ * @param address Binary address
+ * @param zone Zone value-result parameter to fill on success
+ * @return 0 on success or error code on failure
+ */
+extern int AnodeAddress_get_zone(const AnodeAddress *address,AnodeZone *zone);
+
+/**
+ * Convert an address to an ASCII string
+ *
+ * Anode addresses are 64 characters in ASCII form, so the buffer should
+ * have 65 bytes of space.
+ *
+ * @param address Address to convert
+ * @param buf Buffer to receive address in string form (should have 65 bytes of space)
+ * @param len Length of buffer
+ * @return Length of resulting string or a negative error code on error
+ */
+extern int AnodeAddress_to_string(const AnodeAddress *address,char *buf,int len);
+
+/**
+ * Convert a string into an address
+ *
+ * @param str Address in string form
+ * @param address Address buffer to receive result
+ * @return Zero on sucess or error code on error
+ */
+extern int AnodeAddress_from_string(const char *str,AnodeAddress *address);
+
+/**
+ * Supported network address types
+ */
+enum AnodeNetworkAddressType
+{
+ ANODE_NETWORK_ADDRESS_IPV4 = 0,
+ ANODE_NETWORK_ADDRESS_IPV6 = 1,
+ ANODE_NETWORK_ADDRESS_ETHERNET = 2, /* reserved but unused */
+ ANODE_NETWORK_ADDRESS_USB = 3, /* reserved but unused */
+ ANODE_NETWORK_ADDRESS_BLUETOOTH = 4, /* reserved but unused */
+ ANODE_NETWORK_ADDRESS_IPC = 5, /* reserved but unused */
+ ANODE_NETWORK_ADDRESS_80211S = 6, /* reserved but unused */
+ ANODE_NETWORK_ADDRESS_SERIAL = 7, /* reserved but unused */
+ ANODE_NETWORK_ADDRESS_ANODE_256_40 = 8
+};
+
+/**
+ * Anode network address
+ *
+ * This can contain an address of any type: IPv4, IPv6, or Anode, and is used
+ * with the common transport API.
+ *
+ * The length of the address stored in bits[] is determined by the type.
+ */
+typedef struct
+{
+ enum AnodeNetworkAddressType type;
+ char bits[ANODE_ADDRESS_MAX_LENGTH];
+} AnodeNetworkAddress;
+
+/**
+ * An endpoint with an address and a port
+ */
+typedef struct
+{
+ AnodeNetworkAddress address;
+ int port;
+} AnodeNetworkEndpoint;
+
+/* Constants for binding to any address (v4 or v6) */
+extern const AnodeNetworkAddress AnodeNetworkAddress_IP_ANY_V4;
+extern const AnodeNetworkAddress AnodeNetworkAddress_IP_ANY_V6;
+
+/* Local host address in v4 and v6 */
+extern const AnodeNetworkAddress AnodeNetworkAddress_IP_LOCAL_V4;
+extern const AnodeNetworkAddress AnodeNetworkAddress_IP_LOCAL_V6;
+
+/**
+ * Convert a network address to an ASCII string
+ *
+ * The buffer must have room for a 15 character string for IPv4, a 40 byte
+ * string for IPv6, and a 64 byte string for Anode addresses. This does not
+ * include the trailing null.
+ *
+ * @param address Address to convert
+ * @param buf Buffer to receive address in string form
+ * @param len Length of buffer
+ * @return Length of resulting string or a negative error code on error
+ */
+extern int AnodeNetworkAddress_to_string(const AnodeNetworkAddress *address,char *buf,int len);
+
+/**
+ * Convert a string into a network address of the correct type
+ *
+ * @param str Address in string form
+ * @param address Address buffer to receive result
+ * @return Zero on sucess or error code on error
+ */
+extern int AnodeNetworkAddress_from_string(const char *str,AnodeNetworkAddress *address);
+
+/**
+ * Fill a network endpoint from a C-API sockaddr structure
+ *
+ * The argument must be struct sockaddr_in for IPv4 or sockaddr_in6 for IPv6.
+ * The common sin_family field will be used to differentiate.
+ *
+ * @param sockaddr Pointer to proper sockaddr structure
+ * @param endpoint Endpoint structure to fill
+ * @return Zero on success or error on failure
+ */
+extern int AnodeNetworkEndpoint_from_sockaddr(const void *sockaddr,AnodeNetworkEndpoint *endpoint);
+
+/**
+ * Fill a sockaddr from a network endpoint
+ *
+ * To support either IPv4 or IPv6 addresses, there is a sockaddr_storage
+ * structure in most C APIs. If you supply anything other than an IP address
+ * such as an Anode address, this will return an error.
+ *
+ * @param endpoint Endpoint structure to convert
+ * @param sockaddr Sockaddr structure storage
+ * @param sockaddr_len Length of sockaddr structure storage in bytes
+ * @return Zero on success or error on failure
+ */
+extern int AnodeNetworkEndpoint_to_sockaddr(const AnodeNetworkEndpoint *endpoint,void *sockaddr,int sockaddr_len);
+
+/* ----------------------------------------------------------------------- */
+/* Identity Generation and Management */
+/* ----------------------------------------------------------------------- */
+
+/**
+ * Anode identity structure containing address and secret key
+ *
+ * This structure is memcpy-safe, and its members are accessible.
+ */
+typedef struct
+{
+ /* The public Anode address */
+ AnodeAddress address;
+
+ /* Short address ID */
+ AnodeAddressId address_id;
+
+ /* The secret key corresponding with the public address */
+ /* Secret length is determined by address type */
+ char secret[ANODE_ADDRESS_MAX_SECRET_LENGTH];
+} AnodeIdentity;
+
+/**
+ * Generate a new identity
+ *
+ * This generates a public/private key pair and from that generates an
+ * identity containing an address and a secret key.
+ *
+ * @param identity Destination structure to store new identity
+ * @param zone Zone ID
+ * @param type Type of identity to generate
+ * @return Zero on success, error on failure
+ */
+extern int AnodeIdentity_generate(
+ AnodeIdentity *identity,
+ const AnodeZone *zone,
+ enum AnodeAddressType type);
+
+/**
+ * Convert an Anode identity to a string representation
+ *
+ * @param identity Identity to convert
+ * @param dest String buffer
+ * @param dest_len Length of string buffer
+ * @return Length of string created or negative error code on failure
+ */
+extern int AnodeIdentity_to_string(
+ const AnodeIdentity *identity,
+ char *dest,
+ int dest_len);
+
+/**
+ * Convert a string representation to an Anode identity structure
+ *
+ * @param identity Destination structure to fill
+ * @param str C-string containing string representation
+ * @return Zero on success or negative error code on failure
+ */
+extern int AnodeIdentity_from_string(
+ AnodeIdentity *identity,
+ const char *str);
+
+/* ----------------------------------------------------------------------- */
+/* Transport API */
+/* ----------------------------------------------------------------------- */
+
+struct _AnodeTransport;
+typedef struct _AnodeTransport AnodeTransport;
+struct _AnodeEvent;
+typedef struct _AnodeEvent AnodeEvent;
+
+/**
+ * Anode socket
+ */
+typedef struct
+{
+ /* Type of socket (read-only) */
+ enum {
+ ANODE_SOCKET_DATAGRAM = 1,
+ ANODE_SOCKET_STREAM_LISTEN = 2,
+ ANODE_SOCKET_STREAM_CONNECTION = 3
+ } type;
+
+ /* Socket state */
+ enum {
+ ANODE_SOCKET_CLOSED = 0,
+ ANODE_SOCKET_OPEN = 1,
+ ANODE_SOCKET_CONNECTING = 2,
+ } state;
+
+ /* Local address or remote address for stream connections (read-only) */
+ AnodeNetworkEndpoint endpoint;
+
+ /* Name of owning class (read-only) */
+ const char *class_name;
+
+ /* Pointers for end user use (writable) */
+ void *user_ptr[2];
+
+ /* Special handler to receive events or null for default (writable) */
+ void (*event_handler)(const AnodeEvent *event);
+} AnodeSocket;
+
+/**
+ * Anode transport I/O event
+ */
+struct _AnodeEvent
+{
+ enum {
+ ANODE_TRANSPORT_EVENT_DATAGRAM_RECEIVED = 1,
+ ANODE_TRANSPORT_EVENT_STREAM_INCOMING_CONNECT = 2,
+ ANODE_TRANSPORT_EVENT_STREAM_OUTGOING_CONNECT_ESTABLISHED = 3,
+ ANODE_TRANSPORT_EVENT_STREAM_OUTGOING_CONNECT_FAILED = 4,
+ ANODE_TRANSPORT_EVENT_STREAM_CLOSED = 5,
+ ANODE_TRANSPORT_EVENT_STREAM_DATA_RECEIVED = 6,
+ ANODE_TRANSPORT_EVENT_STREAM_AVAILABLE_FOR_WRITE = 7,
+ ANODE_TRANSPORT_EVENT_DNS_RESULT = 8
+ } type;
+
+ AnodeTransport *transport;
+
+ /* Anode socket corresponding to this event */
+ AnodeSocket *sock;
+
+ /* Originating endpoint for incoming datagrams */
+ AnodeNetworkEndpoint *datagram_from;
+
+ /* DNS lookup results */
+ const char *dns_name;
+ AnodeNetworkAddress *dns_addresses;
+ int dns_address_count;
+
+ /* Error code or 0 for none */
+ int error_code;
+
+ /* Data for incoming datagrams and stream received events */
+ int data_length;
+ char *data;
+};
+
+/**
+ * Enum used for dns_resolve method in transport to specify query rules
+ *
+ * This can be specified for ipv4, ipv6, and Anode address types to tell the
+ * DNS resolver when to bother querying for addresses of the given type.
+ * NEVER means to never query for this type, and ALWAYS means to always
+ * query. IF_NO_PREVIOUS means to query for this type if no addresses were
+ * found in previous queries. Addresses are queried in the order of ipv4,
+ * ipv6, then Anode, so if you specify IF_NO_PREVIOUS for all three you will
+ * get addresses in that order of priority.
+ */
+enum AnodeTransportDnsIncludeMode
+{
+ ANODE_TRANSPORT_DNS_QUERY_NEVER = 0,
+ ANODE_TRANSPORT_DNS_QUERY_ALWAYS = 1,
+ ANODE_TRANSPORT_DNS_QUERY_IF_NO_PREVIOUS = 2
+};
+
+struct _AnodeTransport
+{
+ /**
+ * Set the default event handler
+ *
+ * @param transport Transport engine
+ * @param event_handler Default event handler
+ */
+ void (*set_default_event_handler)(AnodeTransport *transport,
+ void (*event_handler)(const AnodeEvent *event));
+
+ /**
+ * Enqueue a function to be executed during a subsequent call to poll()
+ *
+ * This can be called from other threads, so it can be used to pass a
+ * message to the I/O thread in multithreaded applications.
+ *
+ * If it is called from the same thread, the function is still queued to be
+ * run later rather than being run instantly.
+ *
+ * The order in which invoked functions are called is undefined.
+ *
+ * @param transport Transport engine
+ * @param ptr Arbitrary pointer to pass to function to be called
+ * @param func Function to be called
+ */
+ void (*invoke)(AnodeTransport *transport,
+ void *ptr,
+ void (*func)(void *));
+
+ /**
+ * Initiate a forward DNS query
+ *
+ * @param transport Transport instance
+ * @param name DNS name to query
+ * @param event_handler Event handler or null for default event path
+ * @param ipv4_include_mode Inclusion mode for IPv4 addresses
+ * @param ipv6_include_mode Inclusion mode for IPv6 addresses
+ * @param anode_include_mode Inclusion mode for Anode addresses
+ */
+ void (*dns_resolve)(AnodeTransport *transport,
+ const char *name,
+ void (*event_handler)(const AnodeEvent *),
+ enum AnodeTransportDnsIncludeMode ipv4_include_mode,
+ enum AnodeTransportDnsIncludeMode ipv6_include_mode,
+ enum AnodeTransportDnsIncludeMode anode_include_mode);
+
+ /**
+ * Open a datagram socket
+ *
+ * @param transport Transport instance
+ * @param local_address Local address to bind
+ * @param local_port Local port to bind
+ * @param error_code Value-result parameter to receive error code on error
+ * @return Listen socket or null if error (check error_code in error case)
+ */
+ AnodeSocket *(*datagram_listen)(AnodeTransport *transport,
+ const AnodeNetworkAddress *local_address,
+ int local_port,
+ int *error_code);
+
+ /**
+ * Open a socket to listen for incoming stream connections
+ *
+ * @param transport Transport instance
+ * @param local_address Local address to bind
+ * @param local_port Local port to bind
+ * @param error_code Value-result parameter to receive error code on error
+ * @return Listen socket or null if error (check error_code in error case)
+ */
+ AnodeSocket *(*stream_listen)(AnodeTransport *transport,
+ const AnodeNetworkAddress *local_address,
+ int local_port,
+ int *error_code);
+
+ /**
+ * Send a datagram to a network endpoint
+ *
+ * @param transport Transport instance
+ * @param socket Originating datagram socket
+ * @param data Data to send
+ * @param data_len Length of data to send
+ * @param to_endpoint Destination endpoint
+ * @return Zero on success or error code on error
+ */
+ int (*datagram_send)(AnodeTransport *transport,
+ AnodeSocket *sock,
+ const void *data,
+ int data_len,
+ const AnodeNetworkEndpoint *to_endpoint);
+
+ /**
+ * Initiate an outgoing stream connection attempt
+ *
+ * For IPv4 and IPv6 addresses, this will initiate a TCP connection. For
+ * Anode addresses, Anode's internal streaming protocol will be used.
+ *
+ * @param transport Transport instance
+ * @param to_endpoint Destination endpoint
+ * @param error_code Error code value-result parameter, filled on error
+ * @return Stream socket object or null on error (check error_code)
+ */
+ AnodeSocket *(*stream_connect)(AnodeTransport *transport,
+ const AnodeNetworkEndpoint *to_endpoint,
+ int *error_code);
+
+ /**
+ * Indicate that you are interested in writing to a stream
+ *
+ * This does nothing if the socket is not a stream connection or is not
+ * connected.
+ *
+ * @param transport Transport instance
+ * @param sock Stream connection
+ */
+ void (*stream_start_writing)(AnodeTransport *transport,
+ AnodeSocket *sock);
+
+ /**
+ * Indicate that you are no longer interested in writing to a stream
+ *
+ * This does nothing if the socket is not a stream connection or is not
+ * connected.
+ *
+ * @param transport Transport instance
+ * @param sock Stream connection
+ */
+ void (*stream_stop_writing)(AnodeTransport *transport,
+ AnodeSocket *sock);
+
+ /**
+ * Send data to a stream connection
+ *
+ * This must be called after a stream is indicated to be ready for writing.
+ * It returns the number of bytes actually written, or a negative error
+ * code on failure.
+ *
+ * A return value of zero can occur here, and simply indicates that nothing
+ * was sent. This may occur with certain network stacks on certain
+ * platforms.
+ *
+ * @param transport Transport engine
+ * @param sock Stream socket
+ * @param data Data to send
+ * @param data_len Maximum data to send in bytes
+ * @return Actual data sent or negative error code on error
+ */
+ int (*stream_send)(AnodeTransport *transport,
+ AnodeSocket *sock,
+ const void *data,
+ int data_len);
+
+ /**
+ * Close a socket
+ *
+ * If the socket is a stream connection in the connected state, this
+ * will generate a stream closed event with a zero error_code to indicate
+ * a normal close.
+ *
+ * @param transport Transport engine
+ * @param sock Socket object
+ */
+ void (*close)(AnodeTransport *transport,
+ AnodeSocket *sock);
+
+ /**
+ * Run main polling loop
+ *
+ * This should be called repeatedly from the I/O thread of your main
+ * process. It blocks until one or more events occur, and then returns
+ * the number of events. Error returns here are fatal and indicate
+ * serious problems such as build or platform issues or a lack of any
+ * network interface.
+ *
+ * Functions queued with invoke() are also called inside here.
+ *
+ * @param transport Transport engine
+ * @return Number of events handled or negative on (fatal) error
+ */
+ int (*poll)(AnodeTransport *transport);
+
+ /**
+ * Check whether transport supports an address type
+ *
+ * Inheriting classes should call their base if they do not natively
+ * speak the specified type.
+ *
+ * @param transport Transport engine
+ * @param at Address type
+ * @return Nonzero if true
+ */
+ int (*supports_address_type)(const AnodeTransport *transport,
+ enum AnodeNetworkAddressType at);
+
+ /**
+ * Get the instance of AnodeTransport under this one (if any)
+ *
+ * @param transport Transport engine
+ * @return Base instance or null if none
+ */
+ AnodeTransport *(*base_instance)(const AnodeTransport *transport);
+
+ /**
+ * @param transport Transport engine
+ * @return Class name of this instance
+ */
+ const char *(*class_name)(AnodeTransport *transport);
+
+ /**
+ * Delete this transport and its base transports
+ *
+ * The 'transport' pointer and any streams or sockets it owns are no longer
+ * valid after this call.
+ *
+ * @param transport Transport engine
+ */
+ void (*delete)(AnodeTransport *transport);
+};
+
+/**
+ * Construct a new system transport
+ *
+ * This is the default base for AnodeTransport, and it is constructed
+ * automatically if 'base' is null in AnodeTransport_new(). However, it also
+ * exposed to the user so that specialized transports (such as those that use
+ * proxy servers) can be developed on top of it. These in turn can be supplied
+ * as 'base' to AnodeTransport_new() to talk Anode over these transports.
+ *
+ * The system transport supports IP protocols and possibly others.
+ *
+ * @param base Base class or null for none (usually null)
+ * @return Base transport engine instance
+ */
+extern AnodeTransport *AnodeSystemTransport_new(AnodeTransport *base);
+
+/**
+ * Construct a new Anode core transport
+ *
+ * This is the transport that talks Anode using the specified base transport.
+ * Requests for other address types are passed through to the base. If the
+ * base is null, an instance of AnodeSystemTransport is used.
+ *
+ * Since transport engines inherit their functionality, this transport
+ * will also do standard IP and everything else that the system transport
+ * supports. Most users will just want to construct this with a null base.
+ *
+ * @param base Base transport to use, or null to use SystemTransport
+ * @return Anode transport engine or null on error
+ */
+extern AnodeTransport *AnodeCoreTransport_new(AnodeTransport *base);
+
+/* ----------------------------------------------------------------------- */
+/* URI Parser */
+/* ----------------------------------------------------------------------- */
+
+/**
+ * URI broken down by component
+ */
+typedef struct
+{
+ char scheme[8];
+ char username[64];
+ char password[64];
+ char host[128];
+ char path[256];
+ char query[256];
+ char fragment[64];
+ int port;
+} AnodeURI;
+
+/**
+ * URI parser
+ *
+ * A buffer too small error will occur if any field is too large for the
+ * AnodeURI structure.
+ *
+ * @param parsed_uri Structure to fill with parsed URI data
+ * @param uri_string URI in string format
+ * @return Zero on success or error on failure
+ */
+extern int AnodeURI_parse(AnodeURI *parsed_uri,const char *uri_string);
+
+/**
+ * Output a URI in string format
+ *
+ * @param uri URI to output as string
+ * @param buf Buffer to store URI string
+ * @param len Length of buffer
+ * @return Buffer or null on error
+ */
+extern char *AnodeURI_to_string(const AnodeURI *uri,char *buf,int len);
+
+/* ----------------------------------------------------------------------- */
+/* Zone File Lookup and Dictionary */
+/* ----------------------------------------------------------------------- */
+
+/**
+ * Zone file dictionary
+ */
+typedef void AnodeZoneFile;
+
+/**
+ * Start asynchronous zone fetch
+ *
+ * When the zone is retrieved, the lookup handler is called. If zone lookup
+ * failed, the zone file argument to the handler will be null.
+ *
+ * @param transport Transport engine
+ * @param zone Zone ID
+ * @param user_ptr User pointer
+ * @param zone_lookup_handler Handler for Anode zone lookup
+ */
+extern void AnodeZoneFile_lookup(
+ AnodeTransport *transport,
+ const AnodeZone *zone,
+ void *ptr,
+ void (*zone_lookup_handler)(const AnodeZone *,AnodeZoneFile *,void *));
+
+/**
+ * Look up a key in a zone file
+ *
+ * @param zone Zone file object
+ * @param key Key to get in zone file
+ */
+extern const char *AnodeZoneFile_get(const AnodeZoneFile *zone,const char *key);
+
+/**
+ * Free a zone file
+ *
+ * @param zone Zone to free
+ */
+extern void AnodeZoneFile_free(AnodeZoneFile *zone);
+
+/* ----------------------------------------------------------------------- */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/attic/historic/anode/libanode/errors.c b/attic/historic/anode/libanode/errors.c
new file mode 100644
index 00000000..6836bdc4
--- /dev/null
+++ b/attic/historic/anode/libanode/errors.c
@@ -0,0 +1,52 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "anode.h"
+
+struct AnodeErrDesc
+{
+ int code;
+ const char *desc;
+};
+
+#define TOTAL_ERRORS 12
+static const struct AnodeErrDesc ANODE_ERRORS[TOTAL_ERRORS] = {
+ { ANODE_ERR_NONE, "No error (success)" },
+ { ANODE_ERR_INVALID_ARGUMENT, "Invalid argument" },
+ { ANODE_ERR_OUT_OF_MEMORY, "Out of memory" },
+ { ANODE_ERR_INVALID_URI, "Invalid URI" },
+ { ANODE_ERR_BUFFER_TOO_SMALL, "Supplied buffer too small" },
+ { ANODE_ERR_ADDRESS_INVALID, "Address invalid" },
+ { ANODE_ERR_ADDRESS_TYPE_NOT_SUPPORTED, "Address type not supported"},
+ { ANODE_ERR_CONNECTION_CLOSED, "Connection closed"},
+ { ANODE_ERR_CONNECT_FAILED, "Connect failed"},
+ { ANODE_ERR_UNABLE_TO_BIND, "Unable to bind to address"},
+ { ANODE_ERR_TOO_MANY_OPEN_SOCKETS, "Too many open sockets"},
+ { ANODE_ERR_DNS_NAME_NOT_FOUND_OR_TIMED_OUT, "DNS name not found or timed out"}
+};
+
+extern const char *Anode_strerror(int err)
+{
+ int i;
+ int negerr = -err;
+
+ for(i=0;i<TOTAL_ERRORS;++i) {
+ if ((ANODE_ERRORS[i].code == err)||(ANODE_ERRORS[i].code == negerr))
+ return ANODE_ERRORS[i].desc;
+ }
+
+ return "Unknown error";
+}
diff --git a/attic/historic/anode/libanode/identity.c b/attic/historic/anode/libanode/identity.c
new file mode 100644
index 00000000..a40f6987
--- /dev/null
+++ b/attic/historic/anode/libanode/identity.c
@@ -0,0 +1,110 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "impl/types.h"
+#include "impl/ec.h"
+#include "impl/misc.h"
+#include "anode.h"
+
+int AnodeIdentity_generate(AnodeIdentity *identity,const AnodeZone *zone,enum AnodeAddressType type)
+{
+ struct AnodeECKeyPair kp;
+
+ switch(type) {
+ case ANODE_ADDRESS_ANODE_256_40:
+ if (!AnodeECKeyPair_generate(&kp))
+ return ANODE_ERR_OUT_OF_MEMORY;
+
+ identity->address.bits[0] = (unsigned char)ANODE_ADDRESS_ANODE_256_40;
+
+ identity->address.bits[1] = zone->bits[0];
+ identity->address.bits[2] = zone->bits[1];
+ identity->address.bits[3] = zone->bits[2];
+ identity->address.bits[4] = zone->bits[3];
+
+ identity->address.bits[5] = 0;
+ identity->address.bits[6] = 0;
+
+ Anode_memcpy((void *)&(identity->address.bits[7]),(const void *)kp.pub.key,ANODE_EC_PUBLIC_KEY_BYTES);
+ Anode_memcpy((void *)identity->secret,(const void *)kp.priv.key,kp.priv.bytes);
+
+ AnodeAddress_calc_short_id(&identity->address,&identity->address_id);
+
+ AnodeECKeyPair_destroy(&kp);
+
+ return 0;
+ }
+
+ return ANODE_ERR_INVALID_ARGUMENT;
+}
+
+int AnodeIdentity_to_string(const AnodeIdentity *identity,char *dest,int dest_len)
+{
+ char hexbuf[128];
+ char strbuf[128];
+ int n;
+
+ if ((n = AnodeAddress_to_string(&identity->address,strbuf,sizeof(strbuf))) <= 0)
+ return n;
+
+ switch(AnodeAddress_get_type(&identity->address)) {
+ case ANODE_ADDRESS_ANODE_256_40:
+ Anode_to_hex((const unsigned char *)identity->secret,ANODE_ADDRESS_SECRET_LENGTH_ANODE_256_40,hexbuf,sizeof(hexbuf));
+ n = snprintf(dest,dest_len,"ANODE-256-40:%s:%s",strbuf,hexbuf);
+ if (n >= dest_len)
+ return ANODE_ERR_BUFFER_TOO_SMALL;
+ return n;
+ }
+
+ return ANODE_ERR_INVALID_ARGUMENT;
+}
+
+int AnodeIdentity_from_string(AnodeIdentity *identity,const char *str)
+{
+ char buf[1024];
+ char *id_name;
+ char *address;
+ char *secret;
+ int ec;
+
+ Anode_str_copy(buf,str,sizeof(buf));
+
+ id_name = buf;
+ if (!id_name) return 0;
+ if (!*id_name) return 0;
+ address = (char *)Anode_strchr(id_name,':');
+ if (!address) return 0;
+ if (!*address) return 0;
+ *(address++) = (char)0;
+ secret = (char *)Anode_strchr(address,':');
+ if (!secret) return 0;
+ if (!*secret) return 0;
+ *(secret++) = (char)0;
+
+ if (Anode_strcaseeq("ANODE-256-40",id_name)) {
+ if ((ec = AnodeAddress_from_string(address,&identity->address)))
+ return ec;
+ if (Anode_strlen(secret) != (ANODE_ADDRESS_SECRET_LENGTH_ANODE_256_40 * 2))
+ return ANODE_ERR_INVALID_ARGUMENT;
+ Anode_from_hex(secret,(unsigned char *)identity->secret,sizeof(identity->secret));
+ AnodeAddress_calc_short_id(&identity->address,&identity->address_id);
+ return 0;
+ }
+
+ return ANODE_ERR_INVALID_ARGUMENT;
+}
diff --git a/attic/historic/anode/libanode/impl/aes.c b/attic/historic/anode/libanode/impl/aes.c
new file mode 100644
index 00000000..90e5e23b
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/aes.c
@@ -0,0 +1,72 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "aes.h"
+
+void Anode_cmac_aes256(
+ const AnodeAesExpandedKey *expkey,
+ const unsigned char *restrict data,
+ unsigned long data_len,
+ unsigned char *restrict mac)
+{
+ unsigned char cbc[16];
+ unsigned char pad[16];
+ const unsigned char *restrict pos = data;
+ unsigned long i;
+ unsigned long remaining = data_len;
+ unsigned char c;
+
+ ((uint64_t *)((void *)cbc))[0] = 0ULL;
+ ((uint64_t *)((void *)cbc))[1] = 0ULL;
+
+ while (remaining >= 16) {
+ ((uint64_t *)((void *)cbc))[0] ^= ((uint64_t *)((void *)pos))[0];
+ ((uint64_t *)((void *)cbc))[1] ^= ((uint64_t *)((void *)pos))[1];
+ pos += 16;
+ if (remaining > 16)
+ Anode_aes256_encrypt(expkey,cbc,cbc);
+ remaining -= 16;
+ }
+
+ ((uint64_t *)((void *)pad))[0] = 0ULL;
+ ((uint64_t *)((void *)pad))[1] = 0ULL;
+ Anode_aes256_encrypt(expkey,pad,pad);
+
+ c = pad[0] & 0x80;
+ for(i=0;i<15;++i)
+ pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7);
+ pad[15] <<= 1;
+ if (c)
+ pad[15] ^= 0x87;
+
+ if (remaining||(!data_len)) {
+ for(i=0;i<remaining;++i)
+ cbc[i] ^= *(pos++);
+ cbc[remaining] ^= 0x80;
+
+ c = pad[0] & 0x80;
+ for(i=0;i<15;++i)
+ pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7);
+ pad[15] <<= 1;
+ if (c)
+ pad[15] ^= 0x87;
+ }
+
+ ((uint64_t *)((void *)mac))[0] = ((uint64_t *)((void *)pad))[0] ^ ((uint64_t *)((void *)cbc))[0];
+ ((uint64_t *)((void *)mac))[1] = ((uint64_t *)((void *)pad))[1] ^ ((uint64_t *)((void *)cbc))[1];
+
+ Anode_aes256_encrypt(expkey,mac,mac);
+}
diff --git a/attic/historic/anode/libanode/impl/aes.h b/attic/historic/anode/libanode/impl/aes.h
new file mode 100644
index 00000000..25e33933
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/aes.h
@@ -0,0 +1,64 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _ANODE_AES_H
+#define _ANODE_AES_H
+
+#include <openssl/aes.h>
+#include "types.h"
+
+/* This just glues us to OpenSSL's built-in AES-256 implementation */
+
+#define ANODE_AES_BLOCK_SIZE 16
+#define ANODE_AES_KEY_SIZE 32
+
+typedef AES_KEY AnodeAesExpandedKey;
+
+#define Anode_aes256_expand_key(k,ek) AES_set_encrypt_key((const unsigned char *)(k),256,(AES_KEY *)(ek))
+
+/* Note: in and out can be the same thing */
+#define Anode_aes256_encrypt(ek,in,out) AES_encrypt((const unsigned char *)(in),(unsigned char *)(out),(const AES_KEY *)(ek))
+
+/* Note: iv is modified */
+static inline void Anode_aes256_cfb_encrypt(
+ const AnodeAesExpandedKey *expkey,
+ const unsigned char *in,
+ unsigned char *out,
+ unsigned char *iv,
+ unsigned long len)
+{
+ int tmp = 0;
+ AES_cfb128_encrypt(in,out,len,(const AES_KEY *)expkey,iv,&tmp,AES_ENCRYPT);
+}
+static inline void Anode_aes256_cfb_decrypt(
+ const AnodeAesExpandedKey *expkey,
+ const unsigned char *in,
+ unsigned char *out,
+ unsigned char *iv,
+ unsigned long len)
+{
+ int tmp = 0;
+ AES_cfb128_encrypt(in,out,len,(const AES_KEY *)expkey,iv,&tmp,AES_DECRYPT);
+}
+
+/* CMAC message authentication code */
+void Anode_cmac_aes256(
+ const AnodeAesExpandedKey *expkey,
+ const unsigned char *restrict data,
+ unsigned long data_len,
+ unsigned char *restrict mac);
+
+#endif
diff --git a/attic/historic/anode/libanode/impl/dictionary.c b/attic/historic/anode/libanode/impl/dictionary.c
new file mode 100644
index 00000000..060c3815
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/dictionary.c
@@ -0,0 +1,239 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "dictionary.h"
+
+static const char *EMPTY_STR = "";
+
+void AnodeDictionary_clear(struct AnodeDictionary *d)
+{
+ struct AnodeDictionaryEntry *e,*ne;
+ int oldcs;
+ unsigned int i;
+
+ oldcs = d->case_sensitive;
+
+ for(i=0;i<ANODE_DICTIONARY_FIXED_HASH_TABLE_SIZE;++i) {
+ e = d->ht[i];
+ while (e) {
+ ne = e->next;
+ if ((e->key)&&(e->key != EMPTY_STR)) free((void *)e->key);
+ if ((e->value)&&(e->value != EMPTY_STR)) free((void *)e->value);
+ free((void *)e);
+ e = ne;
+ }
+ }
+
+ Anode_zero((void *)d,sizeof(struct AnodeDictionary));
+
+ d->case_sensitive = oldcs;
+}
+
+void AnodeDictionary_put(struct AnodeDictionary *d,const char *key,const char *value)
+{
+ struct AnodeDictionaryEntry *e;
+ char *p1;
+ const char *p2;
+ unsigned int bucket = (d->case_sensitive) ? AnodeDictionary__get_bucket(key) : AnodeDictionary__get_bucket_ci(key);
+ unsigned int len,i;
+
+ e = d->ht[bucket];
+ while (e) {
+ if (((d->case_sensitive) ? Anode_streq(key,e->key) : Anode_strcaseeq(key,e->key))) {
+ if (!d->case_sensitive) {
+ p1 = e->key;
+ p2 = key;
+ while (*p2) *(p1++) = *(p2++);
+ }
+
+ len = 0;
+ while (value[len]) ++len;
+ if (len) {
+ if ((e->value)&&(e->value != EMPTY_STR))
+ e->value = (char *)realloc((void *)e->value,len + 1);
+ else e->value = (char *)malloc(len + 1);
+ for(i=0;i<len;++i) e->value[i] = value[i];
+ e->value[i] = (char)0;
+ } else {
+ if ((e->value)&&(e->value != EMPTY_STR)) free((void *)e->value);
+ e->value = (char *)EMPTY_STR;
+ }
+ return;
+ }
+ e = e->next;
+ }
+
+ e = (struct AnodeDictionaryEntry *)malloc(sizeof(struct AnodeDictionaryEntry));
+
+ len = 0;
+ while (key[len]) ++len;
+ if (len) {
+ e->key = (char *)malloc(len + 1);
+ for(i=0;i<len;++i) e->key[i] = key[i];
+ e->key[i] = (char)0;
+ } else e->key = (char *)EMPTY_STR;
+
+ len = 0;
+ while (value[len]) ++len;
+ if (len) {
+ e->value = (char *)malloc(len + 1);
+ for(i=0;i<len;++i) e->value[i] = value[i];
+ e->value[i] = (char)0;
+ } else e->value = (char *)EMPTY_STR;
+
+ e->next = d->ht[bucket];
+ d->ht[bucket] = e;
+
+ ++d->size;
+}
+
+void AnodeDictionary_read(
+ struct AnodeDictionary *d,
+ char *in,
+ const char *line_breaks,
+ const char *kv_breaks,
+ const char *comment_chars,
+ char escape_char,
+ int trim_whitespace_from_keys,
+ int trim_whitespace_from_values)
+{
+ char *line = in;
+ char *key;
+ char *value;
+ char *p1,*p2,*p3;
+ char last = ~escape_char;
+ int eof_state = 0;
+
+ for(;;) {
+ if ((!*in)||((Anode_strchr(line_breaks,*in))&&((last != escape_char)||(!escape_char)))) {
+ if (!*in)
+ eof_state = 1;
+ else *in = (char)0;
+
+ if ((*line)&&((comment_chars)&&(!Anode_strchr(comment_chars,*line)))) {
+ key = line;
+
+ while (*line) {
+ if ((Anode_strchr(kv_breaks,*line))&&((last != escape_char)||(!escape_char))) {
+ *(line++) = (char)0;
+ break;
+ } else last = *(line++);
+ }
+ while ((*line)&&(Anode_strchr(kv_breaks,*line))&&((last != escape_char)||(!escape_char)))
+ last = *(line++);
+ value = line;
+
+ if (escape_char) {
+ p1 = key;
+ while (*p1) {
+ if (*p1 == escape_char) {
+ p2 = p1;
+ p3 = p1 + 1;
+ while (*p3)
+ *(p2++) = *(p3++);
+ *p2 = (char)0;
+ }
+ ++p1;
+ }
+ p1 = value;
+ while (*p1) {
+ if (*p1 == escape_char) {
+ p2 = p1;
+ p3 = p1 + 1;
+ while (*p3)
+ *(p2++) = *(p3++);
+ *p2 = (char)0;
+ }
+ ++p1;
+ }
+ }
+
+ if (trim_whitespace_from_keys)
+ Anode_trim(key);
+ if (trim_whitespace_from_values)
+ Anode_trim(value);
+
+ AnodeDictionary_put(d,key,value);
+ }
+
+ if (eof_state)
+ break;
+ else line = in + 1;
+ }
+ last = *(in++);
+ }
+}
+
+long AnodeDictionary_write(
+ struct AnodeDictionary *d,
+ char *out,
+ long out_size,
+ const char *line_break,
+ const char *kv_break)
+{
+ struct AnodeDictionaryEntry *e;
+ const char *tmp;
+ long ptr = 0;
+ unsigned int bucket;
+
+ if (out_size <= 0)
+ return -1;
+
+ for(bucket=0;bucket<ANODE_DICTIONARY_FIXED_HASH_TABLE_SIZE;++bucket) {
+ e = d->ht[bucket];
+ while (e) {
+ tmp = e->key;
+ if (tmp) {
+ while (*tmp) {
+ out[ptr++] = *tmp++;
+ if (ptr >= (out_size - 1)) return -1;
+ }
+ }
+
+ tmp = kv_break;
+ if (tmp) {
+ while (*tmp) {
+ out[ptr++] = *tmp++;
+ if (ptr >= (out_size - 1)) return -1;
+ }
+ }
+
+ tmp = e->value;
+ if (tmp) {
+ while (*tmp) {
+ out[ptr++] = *tmp++;
+ if (ptr >= (out_size - 1)) return -1;
+ }
+ }
+
+ tmp = line_break;
+ if (tmp) {
+ while (*tmp) {
+ out[ptr++] = *tmp++;
+ if (ptr >= (out_size - 1)) return -1;
+ }
+ }
+
+ e = e->next;
+ }
+ }
+
+ out[ptr] = (char)0;
+
+ return ptr;
+}
diff --git a/attic/historic/anode/libanode/impl/dictionary.h b/attic/historic/anode/libanode/impl/dictionary.h
new file mode 100644
index 00000000..48e1642a
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/dictionary.h
@@ -0,0 +1,126 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* This is a simple string hash table suitable for small tables such as zone
+ * files or HTTP header lists. */
+
+#ifndef _ANODE_DICTIONARY_H
+#define _ANODE_DICTIONARY_H
+
+#include "misc.h"
+
+/* This is a fixed hash table and is designed for relatively small numbers
+ * of keys for things like zone files. */
+#define ANODE_DICTIONARY_FIXED_HASH_TABLE_SIZE 16
+#define ANODE_DICTIONARY_FIXED_HASH_TABLE_MASK 15
+
+/* Computes a hash code for a string and returns the hash bucket */
+static inline unsigned int AnodeDictionary__get_bucket(const char *s)
+{
+ unsigned int hc = 3;
+ while (*s)
+ hc = ((hc << 4) + hc) + (unsigned int)*(s++);
+ return ((hc ^ (hc >> 4)) & ANODE_DICTIONARY_FIXED_HASH_TABLE_MASK);
+}
+/* Case insensitive version of get_bucket */
+static inline unsigned int AnodeDictionary__get_bucket_ci(const char *s)
+{
+ unsigned int hc = 3;
+ while (*s)
+ hc = ((hc << 4) + hc) + (unsigned int)Anode_tolower(*(s++));
+ return ((hc ^ (hc >> 4)) & ANODE_DICTIONARY_FIXED_HASH_TABLE_MASK);
+}
+
+struct AnodeDictionaryEntry
+{
+ char *key;
+ char *value;
+ struct AnodeDictionaryEntry *next;
+};
+
+struct AnodeDictionary
+{
+ struct AnodeDictionaryEntry *ht[ANODE_DICTIONARY_FIXED_HASH_TABLE_SIZE];
+ unsigned int size;
+ int case_sensitive;
+};
+
+static inline void AnodeDictionary_init(struct AnodeDictionary *d,int case_sensitive)
+{
+ Anode_zero((void *)d,sizeof(struct AnodeDictionary));
+ d->case_sensitive = case_sensitive;
+}
+
+void AnodeDictionary_clear(struct AnodeDictionary *d);
+
+static inline void AnodeDictionary_destroy(struct AnodeDictionary *d)
+{
+ AnodeDictionary_clear(d);
+}
+
+void AnodeDictionary_put(struct AnodeDictionary *d,const char *key,const char *value);
+
+static inline const char *AnodeDictionary_get(struct AnodeDictionary *d,const char *key)
+{
+ struct AnodeDictionaryEntry *e;
+ unsigned int bucket = (d->case_sensitive) ? AnodeDictionary__get_bucket(key) : AnodeDictionary__get_bucket_ci(key);
+
+ e = d->ht[bucket];
+ while (e) {
+ if ((d->case_sensitive ? Anode_streq(key,e->key) : Anode_strcaseeq(key,e->key)))
+ return e->value;
+ e = e->next;
+ }
+
+ return (const char *)0;
+}
+
+static inline void AnodeDictionary_iterate(
+ struct AnodeDictionary *d,
+ void *arg,
+ int (*func)(void *,const char *,const char *))
+{
+ struct AnodeDictionaryEntry *e;
+ unsigned int bucket;
+
+ for(bucket=0;bucket<ANODE_DICTIONARY_FIXED_HASH_TABLE_SIZE;++bucket) {
+ e = d->ht[bucket];
+ while (e) {
+ if (!func(arg,e->key,e->value))
+ return;
+ e = e->next;
+ }
+ }
+}
+
+void AnodeDictionary_read(
+ struct AnodeDictionary *d,
+ char *in,
+ const char *line_breaks,
+ const char *kv_breaks,
+ const char *comment_chars,
+ char escape_char,
+ int trim_whitespace_from_keys,
+ int trim_whitespace_from_values);
+
+long AnodeDictionary_write(
+ struct AnodeDictionary *d,
+ char *out,
+ long out_size,
+ const char *line_break,
+ const char *kv_break);
+
+#endif
diff --git a/attic/historic/anode/libanode/impl/dns_txt.c b/attic/historic/anode/libanode/impl/dns_txt.c
new file mode 100644
index 00000000..b5cf1318
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/dns_txt.c
@@ -0,0 +1,93 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <netdb.h>
+#include "dns_txt.h"
+
+#ifndef C_IN
+#define C_IN ns_c_in
+#endif
+#ifndef T_TXT
+#define T_TXT ns_t_txt
+#endif
+
+static volatile int Anode_resolver_initialized = 0;
+
+int Anode_sync_resolve_txt(const char *host,char *txt,unsigned int txt_len)
+{
+ unsigned char answer[16384],*pptr,*end;
+ char name[16384];
+ int len,explen,i;
+
+ if (!Anode_resolver_initialized) {
+ Anode_resolver_initialized = 1;
+ res_init();
+ }
+
+ /* Do not taunt happy fun ball. */
+
+ len = res_search(host,C_IN,T_TXT,answer,sizeof(answer));
+ if (len > 12) {
+ pptr = answer + 12;
+ end = answer + len;
+
+ explen = dn_expand(answer,end,pptr,name,sizeof(name));
+ if (explen > 0) {
+ pptr += explen;
+
+ if ((pptr + 2) >= end) return 2;
+ if (ntohs(*((uint16_t *)pptr)) == T_TXT) {
+ pptr += 4;
+ if (pptr >= end) return 2;
+
+ explen = dn_expand(answer,end,pptr,name,sizeof(name));
+ if (explen > 0) {
+ pptr += explen;
+
+ if ((pptr + 2) >= end) return 2;
+ if (ntohs(*((uint16_t *)pptr)) == T_TXT) {
+ pptr += 10;
+ if (pptr >= end) return 2;
+
+ len = *(pptr++);
+ if (len <= 0) return 2;
+ if ((pptr + len) > end) return 2;
+
+ if (txt_len < (len + 1))
+ return 4;
+ else {
+ for(i=0;i<len;++i)
+ txt[i] = pptr[i];
+ txt[len] = (char)0;
+ return 0;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
diff --git a/attic/historic/anode/libanode/impl/dns_txt.h b/attic/historic/anode/libanode/impl/dns_txt.h
new file mode 100644
index 00000000..252865df
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/dns_txt.h
@@ -0,0 +1,37 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _ANODE_DNS_TXT_H
+#define _ANODE_DNS_TXT_H
+
+/**
+ * Synchronous TXT resolver routine
+ *
+ * Error codes:
+ * 1 - I/O error
+ * 2 - Invalid response
+ * 3 - TXT record not found
+ * 4 - Destination buffer too small for result
+ *
+ * @param host Host name
+ * @param txt Buffer to store TXT result
+ * @param txt_len Size of buffer
+ * @return Zero on success, special error code on failure
+ */
+int Anode_sync_resolve_txt(const char *host,char *txt,unsigned int txt_len);
+
+#endif
+
diff --git a/attic/historic/anode/libanode/impl/ec.c b/attic/historic/anode/libanode/impl/ec.c
new file mode 100644
index 00000000..2604b4a9
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/ec.c
@@ -0,0 +1,219 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <openssl/bn.h>
+#include <openssl/obj_mac.h>
+#include <openssl/rand.h>
+#include <openssl/ec.h>
+#include <openssl/ecdh.h>
+#include <openssl/ecdsa.h>
+#include "types.h"
+#include "misc.h"
+#include "ec.h"
+
+static EC_GROUP *AnodeEC_group = (EC_GROUP *)0;
+
+static void *AnodeEC_KDF(const void *in,size_t inlen,void *out,size_t *outlen)
+{
+ unsigned long i,longest_length;
+
+ if (!*outlen)
+ return out;
+
+ for(i=0;i<(unsigned long)*outlen;++i)
+ ((unsigned char *)out)[i] = (unsigned char)0;
+
+ longest_length = inlen;
+ if (longest_length < *outlen)
+ longest_length = *outlen;
+ for(i=0;i<longest_length;++i)
+ ((unsigned char *)out)[i % (unsigned long)*outlen] ^= ((const unsigned char *)in)[i % (unsigned long)inlen];
+
+ return out;
+}
+
+int AnodeECKeyPair_generate(struct AnodeECKeyPair *pair)
+{
+ EC_KEY *key;
+ int len;
+
+#ifdef HAS_DEV_URANDOM
+ char buf[128];
+ FILE *f = fopen("/dev/urandom","r");
+ if (f) {
+ if (fread(buf,1,sizeof(buf),f) == sizeof(buf))
+ RAND_add(buf,sizeof(buf),sizeof(buf)/2);
+ fclose(f);
+ }
+#endif
+
+ if (!AnodeEC_group) {
+ AnodeEC_group = EC_GROUP_new_by_curve_name(ANODE_EC_GROUP);
+ if (!AnodeEC_group) return 0;
+ }
+
+ key = EC_KEY_new();
+ if (!key) return 0;
+
+ if (!EC_KEY_set_group(key,AnodeEC_group)) {
+ EC_KEY_free(key);
+ return 0;
+ }
+
+ if (!EC_KEY_generate_key(key)) {
+ EC_KEY_free(key);
+ return 0;
+ }
+
+ Anode_zero(pair,sizeof(struct AnodeECKeyPair));
+
+ /* Stuff the private key into priv.key */
+ len = BN_num_bytes(EC_KEY_get0_private_key(key));
+ if ((len > ANODE_EC_PRIME_BYTES)||(len < 0)) {
+ EC_KEY_free(key);
+ return 0;
+ }
+ BN_bn2bin(EC_KEY_get0_private_key(key),&(pair->priv.key[ANODE_EC_PRIME_BYTES - len]));
+ pair->priv.bytes = ANODE_EC_PRIME_BYTES;
+
+ len = EC_POINT_point2oct(AnodeEC_group,EC_KEY_get0_public_key(key),POINT_CONVERSION_COMPRESSED,pair->pub.key,sizeof(pair->pub.key),0);
+ if (len != ANODE_EC_PUBLIC_KEY_BYTES) {
+ EC_KEY_free(key);
+ return 0;
+ }
+ pair->pub.bytes = ANODE_EC_PUBLIC_KEY_BYTES;
+
+ /* Keep a copy of OpenSSL's structure around so we don't have to re-init
+ * it every time we use our key pair structure. */
+ pair->internal_key = key;
+
+ return 1;
+}
+
+int AnodeECKeyPair_init(struct AnodeECKeyPair *pair,const struct AnodeECKey *pub,const struct AnodeECKey *priv)
+{
+ EC_KEY *key;
+ EC_POINT *kxy;
+ BIGNUM *pn;
+
+ if (!AnodeEC_group) {
+ AnodeEC_group = EC_GROUP_new_by_curve_name(ANODE_EC_GROUP);
+ if (!AnodeEC_group) return 0;
+ }
+
+ key = EC_KEY_new();
+ if (!key)
+ return 0;
+
+ if (!EC_KEY_set_group(key,AnodeEC_group)) {
+ EC_KEY_free(key);
+ return 0;
+ }
+
+ /* Grab the private key */
+ if (priv->bytes != ANODE_EC_PRIME_BYTES) {
+ EC_KEY_free(key);
+ return 0;
+ }
+ pn = BN_new();
+ if (!pn) {
+ EC_KEY_free(key);
+ return 0;
+ }
+ if (!BN_bin2bn(priv->key,ANODE_EC_PRIME_BYTES,pn)) {
+ BN_free(pn);
+ EC_KEY_free(key);
+ return 0;
+ }
+ if (!EC_KEY_set_private_key(key,pn)) {
+ BN_free(pn);
+ EC_KEY_free(key);
+ return 0;
+ }
+ BN_free(pn);
+
+ /* Set the public key */
+ if (pub->bytes != ANODE_EC_PUBLIC_KEY_BYTES) {
+ EC_KEY_free(key);
+ return 0;
+ }
+ kxy = EC_POINT_new(AnodeEC_group);
+ if (!kxy) {
+ EC_KEY_free(key);
+ return 0;
+ }
+ EC_POINT_oct2point(AnodeEC_group,kxy,pub->key,ANODE_EC_PUBLIC_KEY_BYTES,0);
+ if (!EC_KEY_set_public_key(key,kxy)) {
+ EC_POINT_free(kxy);
+ EC_KEY_free(key);
+ return 0;
+ }
+ EC_POINT_free(kxy);
+
+ Anode_zero(pair,sizeof(struct AnodeECKeyPair));
+ Anode_memcpy((void *)&(pair->pub),(const void *)pub,sizeof(struct AnodeECKey));
+ Anode_memcpy((void *)&(pair->priv),(const void *)priv,sizeof(struct AnodeECKey));
+ pair->internal_key = key;
+
+ return 1;
+}
+
+void AnodeECKeyPair_destroy(struct AnodeECKeyPair *pair)
+{
+ if (pair) {
+ if (pair->internal_key)
+ EC_KEY_free((EC_KEY *)pair->internal_key);
+ }
+}
+
+int AnodeECKeyPair_agree(const struct AnodeECKeyPair *my_key_pair,const struct AnodeECKey *their_pub_key,unsigned char *key_buf,unsigned int key_len)
+{
+ EC_POINT *pub;
+ int i;
+
+ if (!AnodeEC_group) {
+ AnodeEC_group = EC_GROUP_new_by_curve_name(ANODE_EC_GROUP);
+ if (!AnodeEC_group) return 0;
+ }
+
+ if (!my_key_pair->internal_key)
+ return 0;
+
+ if (their_pub_key->bytes != ANODE_EC_PUBLIC_KEY_BYTES)
+ return 0;
+ pub = EC_POINT_new(AnodeEC_group);
+ if (!pub)
+ return 0;
+ EC_POINT_oct2point(AnodeEC_group,pub,their_pub_key->key,ANODE_EC_PUBLIC_KEY_BYTES,0);
+
+ i = ECDH_compute_key(key_buf,key_len,pub,(EC_KEY *)my_key_pair->internal_key,&AnodeEC_KDF);
+ if (i != (int)key_len) {
+ EC_POINT_free(pub);
+ return 0;
+ }
+
+ EC_POINT_free(pub);
+
+ return 1;
+}
+
+void AnodeEC_random(unsigned char *buf,unsigned int len)
+{
+ RAND_pseudo_bytes(buf,len);
+}
diff --git a/attic/historic/anode/libanode/impl/ec.h b/attic/historic/anode/libanode/impl/ec.h
new file mode 100644
index 00000000..f1262664
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/ec.h
@@ -0,0 +1,61 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Elliptic curve glue -- hides OpenSSL code behind this source module */
+
+#ifndef _ANODE_EC_H
+#define _ANODE_EC_H
+
+#include "misc.h"
+
+/* Right now, only one mode is supported: NIST-P-256. This is the only mode
+ * supported in the spec as well, and should be good for quite some time.
+ * If other modes are needed this code will need to be refactored. */
+
+/* NIST-P-256 prime size in bytes */
+#define ANODE_EC_PRIME_BYTES 32
+
+/* Sizes of key fields */
+#define ANODE_EC_GROUP NID_X9_62_prime256v1
+#define ANODE_EC_PUBLIC_KEY_BYTES (ANODE_EC_PRIME_BYTES + 1)
+#define ANODE_EC_PRIVATE_KEY_BYTES ANODE_EC_PRIME_BYTES
+
+/* Larger of public or private key bytes, used for buffers */
+#define ANODE_EC_MAX_BYTES ANODE_EC_PUBLIC_KEY_BYTES
+
+struct AnodeECKey
+{
+ unsigned char key[ANODE_EC_MAX_BYTES];
+ unsigned int bytes;
+};
+
+struct AnodeECKeyPair
+{
+ struct AnodeECKey pub;
+ struct AnodeECKey priv;
+ void *internal_key;
+};
+
+/* Key management functions */
+int AnodeECKeyPair_generate(struct AnodeECKeyPair *pair);
+int AnodeECKeyPair_init(struct AnodeECKeyPair *pair,const struct AnodeECKey *pub,const struct AnodeECKey *priv);
+void AnodeECKeyPair_destroy(struct AnodeECKeyPair *pair);
+int AnodeECKeyPair_agree(const struct AnodeECKeyPair *my_key_pair,const struct AnodeECKey *their_pub_key,unsigned char *key_buf,unsigned int key_len);
+
+/* Provides access to the secure PRNG used to generate keys */
+void AnodeEC_random(unsigned char *buf,unsigned int len);
+
+#endif
diff --git a/attic/historic/anode/libanode/impl/environment.c b/attic/historic/anode/libanode/impl/environment.c
new file mode 100644
index 00000000..16e8ebe3
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/environment.c
@@ -0,0 +1,118 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "environment.h"
+
+#ifdef WINDOWS
+#include <windows.h>
+#else
+#include <sys/stat.h>
+#include <string.h>
+#endif
+
+static char Anode_cache_base[1024] = { 0 };
+
+const char *Anode_get_cache()
+{
+ if (Anode_cache_base[0])
+ return Anode_cache_base;
+
+#ifdef WINDOWS
+#else
+ char tmp[1024];
+ char home[1024];
+ unsigned int i;
+ struct stat st;
+ const char *_home = getenv("HOME");
+
+ if (!_home)
+ return (const char *)0;
+ for(i=0;i<sizeof(home);++i) {
+ home[i] = _home[i];
+ if (!home[i]) {
+ if (i == 0)
+ return (const char *)0;
+ else if (home[i-1] == ANODE_PATH_SEPARATOR)
+ home[i-1] = (char)0;
+ break;
+ }
+ }
+ if (i == sizeof(home))
+ return (const char *)0;
+
+#ifdef __APPLE__
+ snprintf(tmp,sizeof(tmp),"%s%cLibrary",home,ANODE_PATH_SEPARATOR);
+ tmp[sizeof(tmp)-1] = (char)0;
+ if (!stat(tmp,&st)) {
+ sprintf(tmp,"%s%cLibrary%cCaches",home,ANODE_PATH_SEPARATOR,ANODE_PATH_SEPARATOR);
+ if (stat(tmp,&st)) {
+ if (mkdir(tmp,0700))
+ return (const char *)0;
+ }
+ snprintf(Anode_cache_base,sizeof(Anode_cache_base),"%s%ccom.zerotier.anode",tmp,ANODE_PATH_SEPARATOR);
+ Anode_cache_base[sizeof(Anode_cache_base)-1] = (char)0;
+ if (stat(Anode_cache_base,&st)) {
+ if (mkdir(Anode_cache_base,0700)) {
+ Anode_cache_base[0] = (char)0;
+ return (const char *)0;
+ }
+ }
+ return Anode_cache_base;
+ }
+#endif
+
+ snprintf(tmp,sizeof(tmp),"%s%c.anode",home,ANODE_PATH_SEPARATOR);
+ tmp[sizeof(tmp)-1] = (char)0;
+ if (stat(tmp,&st)) {
+ if (mkdir(tmp,0700)) {
+ Anode_cache_base[0] = (char)0;
+ return (const char *)0;
+ }
+ }
+ snprintf(Anode_cache_base,sizeof(Anode_cache_base),"%s%ccaches",tmp,ANODE_PATH_SEPARATOR);
+ Anode_cache_base[sizeof(Anode_cache_base)-1] = (char)0;
+ if (stat(Anode_cache_base,&st)) {
+ if (mkdir(Anode_cache_base,0700)) {
+ Anode_cache_base[0] = (char)0;
+ return (const char *)0;
+ }
+ }
+
+ return Anode_cache_base;
+#endif
+}
+
+char *Anode_get_cache_sub(const char *cache_subdir,char *buf,unsigned int len)
+{
+ struct stat st;
+ const char *cache_base = Anode_get_cache();
+
+ if (!len)
+ return (char *)0;
+ if (!cache_base)
+ return (char *)0;
+
+ snprintf(buf,len,"%s%c%s",cache_base,ANODE_PATH_SEPARATOR,cache_subdir);
+ buf[len-1] = (char)0;
+ if (stat(buf,&st)) {
+ if (mkdir(buf,0700))
+ return (char *)0;
+ }
+
+ return buf;
+}
diff --git a/node/NonCopyable.hpp b/attic/historic/anode/libanode/impl/environment.h
index 6d4daa86..ecebdc11 100644
--- a/node/NonCopyable.hpp
+++ b/attic/historic/anode/libanode/impl/environment.h
@@ -1,6 +1,5 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -13,26 +12,19 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
-#ifndef ZT_NONCOPYABLE_HPP__
-#define ZT_NONCOPYABLE_HPP__
+#ifndef _ANODE_ENVIRONMENT_H
+#define _ANODE_ENVIRONMENT_H
-namespace ZeroTier {
-
-/**
- * A simple concept that belongs in the C++ language spec
- */
-class NonCopyable
-{
-protected:
- NonCopyable() throw() {}
-private:
- NonCopyable(const NonCopyable&);
- const NonCopyable& operator=(const NonCopyable&);
-};
+#ifdef WINDOWS
+#define ANODE_PATH_SEPARATOR '\\'
+#else
+#define ANODE_PATH_SEPARATOR '/'
+#endif
-} // namespace ZeroTier
+const char *Anode_get_cache();
+char *Anode_get_cache_sub(const char *cache_subdir,char *buf,unsigned int len);
#endif
+
diff --git a/attic/historic/anode/libanode/impl/http_client.c b/attic/historic/anode/libanode/impl/http_client.c
new file mode 100644
index 00000000..a398a585
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/http_client.c
@@ -0,0 +1,558 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include "http_client.h"
+#include "misc.h"
+#include "types.h"
+
+/* How much to increment read buffer at each capacity top? */
+#define ANODE_HTTP_CAPACITY_INCREMENT 4096
+
+static void AnodeHttpClient_close_and_fail(struct AnodeHttpClient *client)
+{
+ if (client->impl.tcp_connection) {
+ client->impl.transport_engine->tcp_close(client->impl.transport_engine,client->impl.tcp_connection);
+ client->impl.tcp_connection = (AnodeTransportTcpConnection *)0;
+ }
+
+ client->response.data_length = 0;
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CLOSED;
+
+ if (client->handler)
+ client->handler(client);
+}
+
+static void AnodeHttpClient_do_initiate_client(struct AnodeHttpClient *client)
+{
+ const char *method = "";
+ long l,i;
+
+ switch(client->method) {
+ case ANODE_HTTP_GET: method = "GET"; break;
+ case ANODE_HTTP_HEAD: method = "HEAD"; break;
+ case ANODE_HTTP_POST: method = "POST"; break;
+ }
+ client->impl.outbuf_len = snprintf((char *)client->impl.outbuf,sizeof(client->impl.outbuf),
+ "%s %s%s%s HTTP/1.1\r\nHost: %s:%d\r\n%s",
+ method,
+ client->uri.path,
+ ((client->uri.query[0]) ? "?" : ""),
+ client->uri.query,
+ client->uri.host,
+ ((client->uri.port > 0) ? client->uri.port : 80),
+ ((client->keepalive) ? "" : "Connection: close\r\n")
+ );
+ if (client->impl.outbuf_len >= (sizeof(client->impl.outbuf) - 2)) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_HEADERS_TOO_LARGE;
+ AnodeHttpClient_close_and_fail(client);
+ return;
+ }
+
+ if (client->method == ANODE_HTTP_POST) {
+ if ((client->data)&&(client->data_length)) {
+ client->impl.outbuf_len += snprintf((char *)client->impl.outbuf + client->impl.outbuf_len,sizeof(client->impl.outbuf) - client->impl.outbuf_len,
+ "Content-Type: %s\r\n",
+ (client->data_content_type ? client->data_content_type : "application/x-www-form-urlencoded")
+ );
+ if (client->impl.outbuf_len >= (sizeof(client->impl.outbuf) - 2)) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_HEADERS_TOO_LARGE;
+ AnodeHttpClient_close_and_fail(client);
+ return;
+ }
+ client->impl.outbuf_len += snprintf((char *)client->impl.outbuf + client->impl.outbuf_len,sizeof(client->impl.outbuf) - client->impl.outbuf_len,
+ "Content-Length: %u\r\n",
+ client->data_length
+ );
+ if (client->impl.outbuf_len >= (sizeof(client->impl.outbuf) - 2)) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_HEADERS_TOO_LARGE;
+ AnodeHttpClient_close_and_fail(client);
+ return;
+ }
+ } else {
+ client->impl.outbuf_len += snprintf((char *)client->impl.outbuf + client->impl.outbuf_len,sizeof(client->impl.outbuf) - client->impl.outbuf_len,
+ "Content-Length: 0\r\n"
+ );
+ if (client->impl.outbuf_len >= (sizeof(client->impl.outbuf) - 2)) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_HEADERS_TOO_LARGE;
+ AnodeHttpClient_close_and_fail(client);
+ return;
+ }
+ }
+ }
+
+ l = AnodeDictionary_write(&(client->headers),(char *)client->impl.outbuf + client->impl.outbuf_len,(long)(sizeof(client->impl.outbuf) - client->impl.outbuf_len - 2),"\r\n",": ");
+ if (l < 0) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_HEADERS_TOO_LARGE;
+ AnodeHttpClient_close_and_fail(client);
+ return;
+ }
+
+ client->impl.outbuf_len += (unsigned int)l;
+ if (client->impl.outbuf_len >= (sizeof(client->impl.outbuf) - 2)) { /* sanity check */
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_HEADERS_TOO_LARGE;
+ AnodeHttpClient_close_and_fail(client);
+ return;
+ }
+
+ client->impl.outbuf[client->impl.outbuf_len++] = '\r';
+ client->impl.outbuf[client->impl.outbuf_len++] = '\n';
+
+ if ((client->method == ANODE_HTTP_POST)&&(client->data)&&(client->data_length)) {
+ i = sizeof(client->impl.outbuf) - client->impl.outbuf_len;
+ if (i > client->data_length)
+ i = client->data_length;
+ Anode_memcpy((client->impl.outbuf + client->impl.outbuf_len),client->data,i);
+ client->impl.request_data_ptr += i;
+ client->impl.outbuf_len += i;
+ }
+
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_SEND;
+ client->impl.transport_engine->tcp_start_writing(client->impl.transport_engine,client->impl.tcp_connection);
+}
+
+static void AnodeHttpClient_tcp_outgoing_connect_handler(
+ AnodeTransportEngine *transport,
+ AnodeTransportTcpConnection *connection,
+ int error_code)
+{
+ struct AnodeHttpClient *client;
+
+ if (!(client = (struct AnodeHttpClient *)(connection->ptr)))
+ return;
+
+ if ((client->impl.phase == ANODE_HTTP_REQUEST_PHASE_CONNECT)&&(!client->impl.freed)) {
+ if (error_code) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_CONNECT_FAILED;
+ AnodeHttpClient_close_and_fail(client);
+ } else {
+ client->impl.tcp_connection = connection;
+ AnodeHttpClient_do_initiate_client(client);
+ }
+ } else transport->tcp_close(transport,connection);
+}
+
+static void AnodeHttpClient_tcp_connection_terminated_handler(
+ AnodeTransportEngine *transport,
+ AnodeTransportTcpConnection *connection,
+ int error_code)
+{
+ struct AnodeHttpClient *client;
+
+ if (!(client = (struct AnodeHttpClient *)(connection->ptr)))
+ return;
+ if (client->impl.freed)
+ return;
+
+ client->response.data_length = 0;
+ client->impl.tcp_connection = (AnodeTransportTcpConnection *)0;
+ if ((client->impl.phase != ANODE_HTTP_REQUEST_PHASE_KEEPALIVE)&&(client->impl.phase != ANODE_HTTP_REQUEST_PHASE_CLOSED)) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_SERVER_CLOSED_CONNECTION;
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CLOSED;
+ AnodeHttpClient_close_and_fail(client);
+ } else client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CLOSED;
+}
+
+static void AnodeHttpClient_tcp_receive_handler(
+ AnodeTransportEngine *transport,
+ AnodeTransportTcpConnection *connection,
+ void *data,
+ unsigned int data_length)
+{
+ struct AnodeHttpClient *client;
+ char *p1,*p2;
+ unsigned int i;
+ long l;
+
+ if (!(client = (struct AnodeHttpClient *)(connection->ptr)))
+ return;
+ if (client->impl.freed) {
+ transport->tcp_close(transport,connection);
+ return;
+ }
+
+ if (!client->response.data)
+ client->response.data = malloc(client->impl.response_data_capacity = ANODE_HTTP_CAPACITY_INCREMENT);
+
+ i = 0;
+ while (i < data_length) {
+ switch(client->impl.read_mode) {
+ case ANODE_HTTP_READ_MODE_WAITING:
+ for(;i<data_length;++i) {
+ if (((const char *)data)[i] == '\n') {
+ ((char *)client->response.data)[client->response.data_length] = (char)0;
+ client->response.data_length = 0;
+
+ p1 = (char *)Anode_strchr((char *)client->response.data,' ');
+ if (!p1)
+ p1 = (char *)Anode_strchr((char *)client->response.data,'\t');
+ if (p1) {
+ while ((*p1 == ' ')||(*p1 == '\t')) ++p1;
+ if (!*p1) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_INVALID_RESPONSE;
+ AnodeHttpClient_close_and_fail(client);
+ return;
+ }
+ p2 = p1 + 1;
+ while (*p2) {
+ if ((*p2 == ' ')||(*p2 == '\t')||(*p2 == '\r')||(*p2 == '\n')) {
+ *p2 = (char)0;
+ break;
+ } else ++p2;
+ }
+ client->response.code = (int)strtol(p1,(char **)0,10);
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_HEADERS;
+ ++i; break; /* Exit inner for() */
+ }
+ } else {
+ ((char *)client->response.data)[client->response.data_length++] = ((const char *)data)[i];
+ if (client->response.data_length >= client->impl.response_data_capacity)
+ client->response.data = realloc(client->response.data,client->impl.response_data_capacity += ANODE_HTTP_CAPACITY_INCREMENT);
+ }
+ }
+ break;
+ case ANODE_HTTP_READ_MODE_HEADERS:
+ case ANODE_HTTP_READ_MODE_CHUNKED_FOOTER:
+ for(;i<data_length;++i) {
+ if (((const char *)data)[i] == '\n') {
+ client->impl.header_line_buf[client->impl.header_line_buf_ptr] = (char)0;
+ client->impl.header_line_buf_ptr = 0;
+
+ if ((!client->impl.header_line_buf[0])||((client->impl.header_line_buf[0] == '\r')&&(!client->impl.header_line_buf[1]))) {
+ /* If the line is empty (or is empty with \r\n as the
+ * line terminator), we're at the end. */
+ if (client->impl.read_mode == ANODE_HTTP_READ_MODE_CHUNKED_FOOTER) {
+ /* If this is a chunked footer, we finally end the
+ * chunked response. */
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_WAITING;
+ if (client->keepalive)
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_KEEPALIVE;
+ else {
+ client->impl.transport_engine->tcp_close(client->impl.transport_engine,client->impl.tcp_connection);
+ client->impl.tcp_connection = (AnodeTransportTcpConnection *)0;
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CLOSED;
+ }
+ if (client->handler)
+ client->handler(client);
+ if (client->impl.freed)
+ return;
+ } else {
+ /* Otherwise, this is a regular header block */
+ if (client->response.code == 100) {
+ /* Ignore 100 Continue messages */
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_WAITING;
+ ++i; break; /* Exit inner for() */
+ } else if ((client->response.code == 200)&&(client->method != ANODE_HTTP_HEAD)) {
+ /* Other messages get their headers parsed to determine
+ * how to read them. */
+ p1 = (char *)AnodeDictionary_get(&(client->response.headers),"transfer-encoding");
+ if ((p1)&&(Anode_strcaseeq(p1,"chunked"))) {
+ /* Chunked encoding enters chunked mode */
+ client->impl.header_line_buf_ptr = 0;
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_CHUNKED_CHUNK_SIZE;
+ ++i; break; /* Exit inner for() */
+ } else {
+ /* Else we must have a Content-Length header */
+ p1 = (char *)AnodeDictionary_get(&(client->response.headers),"content-length");
+ if (!p1) {
+ /* No chunked or content length is not supported */
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_INVALID_RESPONSE;
+ AnodeHttpClient_close_and_fail(client);
+ return;
+ } else {
+ /* Enter block read mode with content length */
+ l = strtol(p1,(char **)0,10);
+ if (l <= 0) {
+ /* Zero length data is all done... */
+ client->impl.expecting_response_length = 0;
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_WAITING;
+ if (client->keepalive)
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_KEEPALIVE;
+ else {
+ client->impl.transport_engine->tcp_close(client->impl.transport_engine,client->impl.tcp_connection);
+ client->impl.tcp_connection = (AnodeTransportTcpConnection *)0;
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CLOSED;
+ }
+
+ if (client->handler)
+ client->handler(client);
+ if (client->impl.freed)
+ return;
+
+ ++i; break; /* Exit inner for() */
+ } else {
+ /* Else start reading... */
+ client->impl.expecting_response_length = (unsigned int)l;
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_BLOCK;
+ ++i; break; /* Exit inner for() */
+ }
+ }
+ }
+ } else {
+ /* HEAD clients or non-200 codes get headers only */
+ client->impl.expecting_response_length = 0;
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_WAITING;
+ if (client->keepalive)
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_KEEPALIVE;
+ else {
+ client->impl.transport_engine->tcp_close(client->impl.transport_engine,client->impl.tcp_connection);
+ client->impl.tcp_connection = (AnodeTransportTcpConnection *)0;
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CLOSED;
+ }
+
+ if (client->handler)
+ client->handler(client);
+ if (client->impl.freed)
+ return;
+
+ ++i; break; /* Exit inner for() */
+ }
+ }
+ } else {
+ /* Otherwise this is another header, add to dictionary */
+ AnodeDictionary_read(
+ &(client->response.headers),
+ client->impl.header_line_buf,
+ "\r\n",
+ ": \t",
+ "",
+ (char)0,
+ 1,
+ 1
+ );
+ }
+ } else {
+ client->impl.header_line_buf[client->impl.header_line_buf_ptr++] = ((const char *)data)[i];
+ if (client->impl.header_line_buf_ptr >= sizeof(client->impl.header_line_buf)) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_INVALID_RESPONSE;
+ AnodeHttpClient_close_and_fail(client);
+ return;
+ }
+ }
+ }
+ break;
+ case ANODE_HTTP_READ_MODE_BLOCK:
+ if ((client->response.data_length + client->impl.expecting_response_length) > client->impl.response_data_capacity)
+ client->response.data = realloc(client->response.data,client->impl.response_data_capacity = (client->response.data_length + client->impl.expecting_response_length));
+
+ for(;((i<data_length)&&(client->impl.expecting_response_length));++i) {
+ ((char *)client->response.data)[client->response.data_length++] = ((const char *)data)[i];
+ --client->impl.expecting_response_length;
+ }
+
+ if (!client->impl.expecting_response_length) {
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_WAITING;
+ if (client->keepalive)
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_KEEPALIVE;
+ else {
+ client->impl.transport_engine->tcp_close(client->impl.transport_engine,client->impl.tcp_connection);
+ client->impl.tcp_connection = (AnodeTransportTcpConnection *)0;
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CLOSED;
+ }
+
+ if (client->handler)
+ client->handler(client);
+ if (client->impl.freed)
+ return;
+ }
+ break;
+ case ANODE_HTTP_READ_MODE_CHUNKED_CHUNK_SIZE:
+ for(;i<data_length;++i) {
+ if (((const char *)data)[i] == '\n') {
+ client->impl.header_line_buf[client->impl.header_line_buf_ptr] = (char)0;
+ client->impl.header_line_buf_ptr = 0;
+
+ p1 = client->impl.header_line_buf;
+ while (*p1) {
+ if ((*p1 == ';')||(*p1 == ' ')||(*p1 == '\r')||(*p1 == '\n')||(*p1 == '\t')) {
+ *p1 = (char)0;
+ break;
+ } else ++p1;
+ }
+
+ if (client->impl.header_line_buf[0]) {
+ l = strtol(client->impl.header_line_buf,(char **)0,16);
+ if (l <= 0) {
+ /* Zero length ends chunked and enters footer mode */
+ client->impl.expecting_response_length = 0;
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_CHUNKED_FOOTER;
+ } else {
+ /* Otherwise the next chunk is to be read */
+ client->impl.expecting_response_length = (unsigned int)l;
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_CHUNKED_DATA;
+ }
+ ++i; break; /* Exit inner for() */
+ }
+ } else {
+ client->impl.header_line_buf[client->impl.header_line_buf_ptr++] = ((const char *)data)[i];
+ if (client->impl.header_line_buf_ptr >= sizeof(client->impl.header_line_buf)) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_INVALID_RESPONSE;
+ AnodeHttpClient_close_and_fail(client);
+ return;
+ }
+ }
+ }
+ break;
+ case ANODE_HTTP_READ_MODE_CHUNKED_DATA:
+ if ((client->response.data_length + client->impl.expecting_response_length) > client->impl.response_data_capacity)
+ client->response.data = realloc(client->response.data,client->impl.response_data_capacity = (client->response.data_length + client->impl.expecting_response_length));
+
+ for(;((i<data_length)&&(client->impl.expecting_response_length));++i) {
+ ((char *)client->response.data)[client->response.data_length++] = ((const char *)data)[i];
+ --client->impl.expecting_response_length;
+ }
+
+ if (!client->impl.expecting_response_length)
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_CHUNKED_CHUNK_SIZE;
+ break;
+ }
+ }
+}
+
+static void AnodeHttpClient_tcp_available_for_write_handler(
+ AnodeTransportEngine *transport,
+ AnodeTransportTcpConnection *connection)
+{
+ struct AnodeHttpClient *client;
+ unsigned int i,j;
+ int n;
+
+ if (!(client = (struct AnodeHttpClient *)(connection->ptr)))
+ return;
+ if (client->impl.freed) {
+ transport->tcp_close(transport,connection);
+ return;
+ }
+
+ if (client->impl.phase == ANODE_HTTP_REQUEST_PHASE_SEND) {
+ n = client->impl.transport_engine->tcp_send(client->impl.transport_engine,client->impl.tcp_connection,(const void *)client->impl.outbuf,(int)client->impl.outbuf_len);
+ if (n < 0) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_SERVER_CLOSED_CONNECTION;
+ AnodeHttpClient_close_and_fail(client);
+ } else if (n > 0) {
+ for(i=0,j=(client->impl.outbuf_len - (unsigned int)n);i<j;++i)
+ client->impl.outbuf[i] = client->impl.outbuf[i + (unsigned int)n];
+ client->impl.outbuf_len -= (unsigned int)n;
+
+ if ((client->method == ANODE_HTTP_POST)&&(client->data)&&(client->data_length)) {
+ i = sizeof(client->impl.outbuf) - client->impl.outbuf_len;
+ j = client->data_length - client->impl.request_data_ptr;
+ if (i > j)
+ i = j;
+ Anode_memcpy((client->impl.outbuf + client->impl.outbuf_len),client->data,i);
+ client->impl.request_data_ptr += i;
+ client->impl.outbuf_len += i;
+ }
+
+ if (!client->impl.outbuf_len) {
+ client->impl.transport_engine->tcp_stop_writing(client->impl.transport_engine,client->impl.tcp_connection);
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_RECEIVE;
+ }
+ }
+ } else client->impl.transport_engine->tcp_stop_writing(client->impl.transport_engine,client->impl.tcp_connection);
+}
+
+static void AnodeHttpClient_dns_result_handler(
+ AnodeTransportEngine *transport,
+ void *ptr,
+ int error_code,
+ const char *name,
+ const AnodeTransportIpAddress *ip_addresses,
+ unsigned int ip_address_count,
+ const AnodeAddress *anode_address)
+{
+ struct AnodeHttpClient *client;
+ AnodeTransportIpEndpoint to_endpoint;
+
+ if (!(client = (struct AnodeHttpClient *)ptr))
+ return;
+ if (client->impl.freed)
+ return;
+
+ if ((error_code)||(!ip_address_count)) {
+ if (client->impl.phase == ANODE_HTTP_REQUEST_PHASE_RESOLVE) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_DNS_RESOLVE_FAILED;
+ AnodeHttpClient_close_and_fail(client);
+ }
+ } else {
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CONNECT;
+ Anode_memcpy(&to_endpoint.address,ip_addresses,sizeof(AnodeTransportIpAddress));
+ to_endpoint.port = (client->uri.port > 0) ? client->uri.port : 80;
+ client->impl.transport_engine->tcp_connect(
+ client->impl.transport_engine,
+ client,
+ &AnodeHttpClient_tcp_outgoing_connect_handler,
+ &AnodeHttpClient_tcp_connection_terminated_handler,
+ &AnodeHttpClient_tcp_receive_handler,
+ &AnodeHttpClient_tcp_available_for_write_handler,
+ &to_endpoint);
+ }
+}
+
+struct AnodeHttpClient *AnodeHttpClient_new(AnodeTransportEngine *transport_engine)
+{
+ struct AnodeHttpClient *req = malloc(sizeof(struct AnodeHttpClient));
+ Anode_zero(req,sizeof(struct AnodeHttpClient));
+
+ AnodeDictionary_init(&(req->headers),0);
+ AnodeDictionary_init(&(req->response.headers),0);
+
+ req->impl.transport_engine = transport_engine;
+
+ return req;
+}
+
+void AnodeHttpClient_send(struct AnodeHttpClient *client)
+{
+ client->response.code = 0;
+ client->response.data_length = 0;
+ AnodeDictionary_clear(&(client->response.headers));
+
+ client->impl.request_data_ptr = 0;
+ client->impl.expecting_response_length = 0;
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_WAITING;
+ client->impl.outbuf_len = 0;
+
+ if (!client->impl.tcp_connection) {
+ client->impl.transport_engine->dns_resolve(
+ client->impl.transport_engine,
+ &AnodeHttpClient_dns_result_handler,
+ client,
+ client->uri.host,
+ ANODE_TRANSPORT_DNS_QUERY_ALWAYS,
+ ANODE_TRANSPORT_DNS_QUERY_IF_NO_PREVIOUS,
+ ANODE_TRANSPORT_DNS_QUERY_NEVER);
+ } else AnodeHttpClient_do_initiate_client(client);
+}
+
+void AnodeHttpClient_free(struct AnodeHttpClient *client)
+{
+ AnodeDictionary_destroy(&(client->headers));
+ AnodeDictionary_destroy(&(client->response.headers));
+
+ if (client->impl.tcp_connection) {
+ client->impl.transport_engine->tcp_close(client->impl.transport_engine,client->impl.tcp_connection);
+ client->impl.tcp_connection = (AnodeTransportTcpConnection *)0;
+ }
+
+ if (client->response.data)
+ free(client->response.data);
+
+ client->impl.freed = 1;
+ client->impl.transport_engine->run_later(client->impl.transport_engine,client,&free);
+}
diff --git a/attic/historic/anode/libanode/impl/http_client.h b/attic/historic/anode/libanode/impl/http_client.h
new file mode 100644
index 00000000..f1673097
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/http_client.h
@@ -0,0 +1,200 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _ANODE_HTTP_CLIENT_H
+#define _ANODE_HTTP_CLIENT_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "dictionary.h"
+#include "../anode.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * HTTP request type
+ */
+enum AnodeHttpClientRequestMethod
+{
+ ANODE_HTTP_GET = 0,
+ ANODE_HTTP_HEAD = 1,
+ ANODE_HTTP_POST = 2
+};
+
+/*
+ * Special response codes to indicate I/O errors
+ */
+#define ANODE_HTTP_SPECIAL_RESPONSE_DNS_RESOLVE_FAILED -1
+#define ANODE_HTTP_SPECIAL_RESPONSE_CONNECT_FAILED -2
+#define ANODE_HTTP_SPECIAL_RESPONSE_HEADERS_TOO_LARGE -3
+#define ANODE_HTTP_SPECIAL_RESPONSE_SERVER_CLOSED_CONNECTION -4
+#define ANODE_HTTP_SPECIAL_RESPONSE_INVALID_RESPONSE -5
+
+/**
+ * Simple HTTP client
+ */
+struct AnodeHttpClient
+{
+ /**
+ * Request URI
+ */
+ AnodeURI uri;
+
+ /**
+ * Request method: GET, PUT, HEAD, or POST
+ */
+ enum AnodeHttpClientRequestMethod method;
+
+ /**
+ * Data for POST requests
+ *
+ * It is your responsibility to manage and/or free this pointer. The HTTP
+ * client only reads from it.
+ */
+ const void *data;
+ unsigned int data_length;
+
+ /**
+ * Content type for data, or null for application/x-www-form-urlencoded
+ */
+ const char *data_content_type;
+
+ /**
+ * Set to non-zero to use HTTP connection keepalive
+ *
+ * If keepalive is enabled, this request can be modified and re-used and
+ * its associated connection will stay open (being reopened if needed)
+ * until it is freed.
+ *
+ * Note that this client is too dumb to pool connections and pick them on
+ * the basis of host. Keepalive mode should only be set if the next request
+ * will be from the same host and port, otherwise you will get a '404'.
+ */
+ int keepalive;
+
+ /**
+ * Function pointer to be called when request is complete (or fails)
+ */
+ void (*handler)(struct AnodeHttpClient *);
+
+ /**
+ * Two arbitrary pointers that can be stored here for use by the handler.
+ * These are not accessed or modified by the client.
+ */
+ void *ptr[2];
+
+ /**
+ * Request headers
+ */
+ struct AnodeDictionary headers;
+
+ struct {
+ /**
+ * Response code, set on completion or failure before handler is called
+ *
+ * Also check for the special response codes defined in http_client.h as
+ * these negative codes indicate network or other errors.
+ */
+ int code;
+
+ /**
+ * Response data, for GET and POST requests
+ */
+ void *data;
+
+ /**
+ * Length of response data
+ */
+ unsigned int data_length;
+
+ /**
+ * Response headers
+ */
+ struct AnodeDictionary headers;
+ } response;
+
+ /**
+ * Internal fields used by implementation
+ */
+ struct {
+ /* Transport engine being used by request */
+ AnodeTransportEngine *transport_engine;
+
+ /* Connection to which request has been sent, or null if none */
+ struct AnodeHttpConnection *connection;
+
+ /* Buffer for reading chunked mode chunk lines (can't use data buf) */
+ char header_line_buf[256];
+ unsigned int header_line_buf_ptr;
+
+ /* Where are we in sending request data? */
+ unsigned int request_data_ptr;
+
+ /* Capacity of response_data buffer */
+ unsigned int response_data_capacity;
+
+ /* How much response data are we currently expecting? */
+ /* This is content-length in block mode or chunk length in chunked mode */
+ unsigned int expecting_response_length;
+
+ /* Read mode */
+ enum {
+ ANODE_HTTP_READ_MODE_WAITING = 0,
+ ANODE_HTTP_READ_MODE_HEADERS = 1,
+ ANODE_HTTP_READ_MODE_BLOCK = 2,
+ ANODE_HTTP_READ_MODE_CHUNKED_CHUNK_SIZE = 3,
+ ANODE_HTTP_READ_MODE_CHUNKED_DATA = 4,
+ ANODE_HTTP_READ_MODE_CHUNKED_FOOTER = 5
+ } read_mode;
+
+ /* Connection from transport engine */
+ AnodeTransportTcpConnection *tcp_connection;
+
+ /* Write buffer */
+ unsigned char outbuf[16384];
+ unsigned int outbuf_len;
+
+ /* Phase of request state machine */
+ enum {
+ ANODE_HTTP_REQUEST_PHASE_RESOLVE = 0,
+ ANODE_HTTP_REQUEST_PHASE_CONNECT = 1,
+ ANODE_HTTP_REQUEST_PHASE_SEND = 2,
+ ANODE_HTTP_REQUEST_PHASE_RECEIVE = 3,
+ ANODE_HTTP_REQUEST_PHASE_KEEPALIVE = 4,
+ ANODE_HTTP_REQUEST_PHASE_CLOSED = 5
+ } phase;
+
+ /* Has request object been freed? */
+ int freed;
+
+ /**
+ * Pointer used internally for putting requests into linked lists
+ */
+ struct AnodeHttpClient *next;
+ } impl;
+};
+
+struct AnodeHttpClient *AnodeHttpClient_new(AnodeTransportEngine *transport_engine);
+void AnodeHttpClient_send(struct AnodeHttpClient *client);
+void AnodeHttpClient_free(struct AnodeHttpClient *client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/attic/historic/anode/libanode/impl/misc.c b/attic/historic/anode/libanode/impl/misc.c
new file mode 100644
index 00000000..edc73978
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/misc.c
@@ -0,0 +1,190 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "misc.h"
+#include "types.h"
+
+static const char Anode_hex_chars[16] = {
+ '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
+};
+
+static const char Anode_base32_chars[32] = {
+ 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q',
+ 'r','s','t','u','v','w','x','y','z','2','3','4','5','6','7'
+};
+static const unsigned char Anode_base32_bits[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,26,27,28,29,30,31,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,
+ 6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,0,0,0,0,0,0,0,1,2,
+ 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+
+/* Table for converting ASCII chars to lower case */
+const unsigned char Anode_ascii_tolower_table[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+};
+
+void Anode_trim(char *s)
+{
+ char *dest = s;
+ char *last;
+ while ((*s)&&((*s == ' ')||(*s == '\t')||(*s == '\r')||(*s == '\n')))
+ ++s;
+ last = s;
+ while ((*dest = *s)) {
+ if ((*dest != ' ')&&(*dest != '\t')&&(*dest != '\r')&&(*dest != '\n'))
+ last = dest;
+ ++dest;
+ ++s;
+ }
+ if (*last)
+ *(++last) = (char)0;
+}
+
+unsigned int Anode_rand()
+{
+ static volatile int need_seed = 1;
+
+ if (need_seed) {
+ need_seed = 0;
+ srandom((unsigned long)Anode_time64());
+ }
+
+ return (unsigned int)random();
+}
+
+void Anode_to_hex(const unsigned char *b,unsigned int len,char *h,unsigned int hlen)
+{
+ unsigned int i;
+
+ if ((len * 2) >= hlen)
+ len = (hlen - 1) / 2;
+
+ for(i=0;i<len;++i) {
+ *(h++) = Anode_hex_chars[b[i] >> 4];
+ *(h++) = Anode_hex_chars[b[i] & 0xf];
+ }
+ *h = (char)0;
+}
+
+void Anode_from_hex(const char *h,unsigned char *b,unsigned int blen)
+{
+ unsigned char *end = b + blen;
+ unsigned char v = (unsigned char)0;
+
+ while (b != end) {
+ switch(*(h++)) {
+ case '0': v = 0x00; break;
+ case '1': v = 0x10; break;
+ case '2': v = 0x20; break;
+ case '3': v = 0x30; break;
+ case '4': v = 0x40; break;
+ case '5': v = 0x50; break;
+ case '6': v = 0x60; break;
+ case '7': v = 0x70; break;
+ case '8': v = 0x80; break;
+ case '9': v = 0x90; break;
+ case 'a': v = 0xa0; break;
+ case 'b': v = 0xb0; break;
+ case 'c': v = 0xc0; break;
+ case 'd': v = 0xd0; break;
+ case 'e': v = 0xe0; break;
+ case 'f': v = 0xf0; break;
+ default: return;
+ }
+
+ switch(*(h++)) {
+ case '0': v |= 0x00; break;
+ case '1': v |= 0x01; break;
+ case '2': v |= 0x02; break;
+ case '3': v |= 0x03; break;
+ case '4': v |= 0x04; break;
+ case '5': v |= 0x05; break;
+ case '6': v |= 0x06; break;
+ case '7': v |= 0x07; break;
+ case '8': v |= 0x08; break;
+ case '9': v |= 0x09; break;
+ case 'a': v |= 0x0a; break;
+ case 'b': v |= 0x0b; break;
+ case 'c': v |= 0x0c; break;
+ case 'd': v |= 0x0d; break;
+ case 'e': v |= 0x0e; break;
+ case 'f': v |= 0x0f; break;
+ default: return;
+ }
+
+ *(b++) = v;
+ }
+}
+
+void Anode_base32_5_to_8(const unsigned char *in,char *out)
+{
+ out[0] = Anode_base32_chars[(in[0]) >> 3];
+ out[1] = Anode_base32_chars[(in[0] & 0x07) << 2 | (in[1] & 0xc0) >> 6];
+ out[2] = Anode_base32_chars[(in[1] & 0x3e) >> 1];
+ out[3] = Anode_base32_chars[(in[1] & 0x01) << 4 | (in[2] & 0xf0) >> 4];
+ out[4] = Anode_base32_chars[(in[2] & 0x0f) << 1 | (in[3] & 0x80) >> 7];
+ out[5] = Anode_base32_chars[(in[3] & 0x7c) >> 2];
+ out[6] = Anode_base32_chars[(in[3] & 0x03) << 3 | (in[4] & 0xe0) >> 5];
+ out[7] = Anode_base32_chars[(in[4] & 0x1f)];
+}
+
+void Anode_base32_8_to_5(const char *in,unsigned char *out)
+{
+ out[0] = ((Anode_base32_bits[(unsigned int)in[0]]) << 3) | (Anode_base32_bits[(unsigned int)in[1]] & 0x1C) >> 2;
+ out[1] = ((Anode_base32_bits[(unsigned int)in[1]] & 0x03) << 6) | (Anode_base32_bits[(unsigned int)in[2]]) << 1 | (Anode_base32_bits[(unsigned int)in[3]] & 0x10) >> 4;
+ out[2] = ((Anode_base32_bits[(unsigned int)in[3]] & 0x0F) << 4) | (Anode_base32_bits[(unsigned int)in[4]] & 0x1E) >> 1;
+ out[3] = ((Anode_base32_bits[(unsigned int)in[4]] & 0x01) << 7) | (Anode_base32_bits[(unsigned int)in[5]]) << 2 | (Anode_base32_bits[(unsigned int)in[6]] & 0x18) >> 3;
+ out[4] = ((Anode_base32_bits[(unsigned int)in[6]] & 0x07) << 5) | (Anode_base32_bits[(unsigned int)in[7]]);
+}
diff --git a/attic/historic/anode/libanode/impl/misc.h b/attic/historic/anode/libanode/impl/misc.h
new file mode 100644
index 00000000..38ddea7c
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/misc.h
@@ -0,0 +1,193 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* This contains miscellaneous functions, including some re-implementations
+ * of some functions from string.h. This is to help us port to some platforms
+ * (cough Windows Mobile cough) that lack a lot of the basic C library. */
+
+#ifndef _ANODE_MISC_H
+#define _ANODE_MISC_H
+
+#include <time.h>
+#include <sys/time.h>
+#include "types.h"
+
+#ifndef ANODE_NO_STRING_H
+#include <string.h>
+#include <stdlib.h>
+#endif
+
+/* Table mapping ASCII characters to themselves or their lower case */
+extern const unsigned char Anode_ascii_tolower_table[256];
+
+/* Get the lower case version of an ASCII char */
+#define Anode_tolower(c) ((char)Anode_ascii_tolower_table[((unsigned long)((unsigned char)(c)))])
+
+/* Test strings for equality, return nonzero if equal */
+static inline unsigned int Anode_streq(const char *restrict a,const char *restrict b)
+{
+ if ((!a)||(!b))
+ return 0;
+ while (*a == *(b++)) {
+ if (!*(a++))
+ return 1;
+ }
+ return 0;
+}
+
+/* Equality test ignoring (ASCII) case */
+static inline unsigned int Anode_strcaseeq(const char *restrict a,const char *restrict b)
+{
+ if ((!a)||(!b))
+ return 0;
+ while (Anode_tolower(*a) == Anode_tolower(*(b++))) {
+ if (!*(a++))
+ return 1;
+ }
+ return 0;
+}
+
+/* Safe c-string copy, ensuring that dest[] always ends with zero */
+static inline void Anode_str_copy(char *restrict dest,const char *restrict src,unsigned int dest_size)
+{
+ char *restrict dest_end = dest + (dest_size - 1);
+ while ((*src)&&(dest != dest_end))
+ *(dest++) = *(src++);
+ *dest = (char)0;
+}
+
+/* Simple memcpy() */
+#ifdef ANODE_NO_STRING_H
+static inline void Anode_memcpy(void *restrict dest,const void *restrict src,unsigned int len)
+{
+ unsigned int i;
+ for(i=0;i<len;++i)
+ ((unsigned char *restrict)dest)[i] = ((const unsigned char *restrict)src)[i];
+}
+#else
+#define Anode_memcpy(d,s,l) memcpy((d),(s),(l))
+#endif
+
+/* Memory test for equality */
+#ifdef ANODE_NO_STRING_H
+static inline unsigned int Anode_mem_eq(const void *restrict a,const void *restrict b,unsigned int len)
+{
+ unsigned int i;
+ for(i=0;i<len;++i) {
+ if (((const unsigned char *restrict)a)[i] != ((const unsigned char *restrict)b)[i])
+ return 0;
+ }
+ return 1;
+}
+#else
+#define Anode_mem_eq(a,b,l) (!memcmp((a),(b),(l)))
+#endif
+
+/* Zero memory */
+#ifdef ANODE_NO_STRING_H
+static inline void Anode_zero(void *restrict ptr,unsigned int len)
+{
+ unsigned int i;
+ for(i=0;i<len;++i)
+ ((unsigned char *restrict)ptr)[i] = (unsigned char)0;
+}
+#else
+#define Anode_zero(p,l) memset((p),0,(l))
+#endif
+
+/* Get a pointer to the first occurrance of a character in a string */
+#ifdef ANODE_NO_STRING_H
+static inline const char *Anode_strchr(const char *s,char c)
+{
+ while (*s) {
+ if (*s == c)
+ return s;
+ ++s;
+ }
+ return (char *)0;
+}
+#else
+#define Anode_strchr(s,c) strchr((s),(c))
+#endif
+
+static inline unsigned int Anode_count_char(const char *s,char c)
+{
+ unsigned int cnt = 0;
+ while (s) {
+ if (*s == c)
+ ++cnt;
+ ++s;
+ }
+ return cnt;
+}
+
+/* Strip all of a given set of characters from a string */
+static inline void Anode_strip_all(char *s,const char *restrict schars)
+{
+ char *d = s;
+
+ while (*s) {
+ if (!Anode_strchr(schars,*s))
+ *(d++) = *s;
+ ++s;
+ }
+ *d = (char)0;
+}
+
+/* Trim whitespace from beginning and end of string */
+void Anode_trim(char *s);
+
+/* Get the length of a string */
+#ifdef ANODE_NO_STRING_H
+static inline unsigned int Anode_strlen(const char *s)
+{
+ const char *ptr = s;
+ while (*ptr) ++ptr;
+ return (unsigned int)(ptr - s);
+}
+#else
+#define Anode_strlen(s) strlen((s))
+#endif
+
+/* Returns number of milliseconds since the epoch (Java-style) */
+static inline uint64_t Anode_time64()
+{
+ struct timeval tv;
+ gettimeofday(&tv,(void *)0);
+ return ( (((uint64_t)tv.tv_sec) / 1000ULL) + ((uint64_t)(tv.tv_usec / 1000ULL)) );
+}
+
+/* Returns number of seconds since the epoch (*nix style) */
+static inline unsigned long Anode_time()
+{
+ struct timeval tv;
+ gettimeofday(&tv,(void *)0);
+ return (unsigned long)tv.tv_sec;
+}
+
+/* Simple random function, not cryptographically safe */
+unsigned int Anode_rand();
+
+/* Fast hex/ascii conversion */
+void Anode_to_hex(const unsigned char *b,unsigned int len,char *h,unsigned int hlen);
+void Anode_from_hex(const char *h,unsigned char *b,unsigned int blen);
+
+/* Convert back and forth from base32 encoding */
+/* 5 bytes -> 8 base32 characters and vice versa */
+void Anode_base32_5_to_8(const unsigned char *in,char *out);
+void Anode_base32_8_to_5(const char *in,unsigned char *out);
+
+#endif
diff --git a/attic/historic/anode/libanode/impl/mutex.h b/attic/historic/anode/libanode/impl/mutex.h
new file mode 100644
index 00000000..b20eb82b
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/mutex.h
@@ -0,0 +1,34 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _ANODE_MUTEX_H
+#define _ANODE_MUTEX_H
+
+#ifdef WINDOWS
+
+#else /* WINDOWS */
+
+#include <pthread.h>
+
+#define AnodeMutex pthread_mutex_t
+#define AnodeMutex_init(m) pthread_mutex_init((m),(const pthread_mutexattr_t *)0)
+#define AnodeMutex_destroy(m) pthread_mutex_destroy((m))
+#define AnodeMutex_lock(m) pthread_mutex_lock((m))
+#define AnodeMutex_unlock(m) pthread_mutex_unlock((m))
+
+#endif /* WINDOWS */
+
+#endif
diff --git a/attic/historic/anode/libanode/impl/thread.c b/attic/historic/anode/libanode/impl/thread.c
new file mode 100644
index 00000000..c2070462
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/thread.c
@@ -0,0 +1,58 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "thread.h"
+#include <stdlib.h>
+
+#ifdef WINDOWS
+
+#else /* not WINDOWS */
+
+struct _AnodeThread
+{
+ void (*func)(void *);
+ void *arg;
+ int wait_for_join;
+ pthread_t thread;
+};
+
+static void *_AnodeThread_main(void *arg)
+{
+ ((struct _AnodeThread *)arg)->func(((struct _AnodeThread *)arg)->arg);
+ if (!((struct _AnodeThread *)arg)->wait_for_join)
+ free(arg);
+ return (void *)0;
+}
+
+AnodeThread *AnodeThread_create(void (*func)(void *),void *arg,int wait_for_join)
+{
+ struct _AnodeThread *t = malloc(sizeof(struct _AnodeThread));
+ t->func = func;
+ t->arg = arg;
+ t->wait_for_join = wait_for_join;
+ pthread_create(&t->thread,(const pthread_attr_t *)0,&_AnodeThread_main,(void *)t);
+ if (!wait_for_join)
+ pthread_detach(t->thread);
+ return (AnodeThread *)t;
+}
+
+void AnodeThread_join(AnodeThread *thread)
+{
+ pthread_join(((struct _AnodeThread *)thread)->thread,(void **)0);
+ free((void *)thread);
+}
+
+#endif /* WINDOWS / not WINDOWS */
diff --git a/attic/historic/anode/libanode/impl/thread.h b/attic/historic/anode/libanode/impl/thread.h
new file mode 100644
index 00000000..accf173a
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/thread.h
@@ -0,0 +1,65 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _ANODE_THREAD_H
+#define _ANODE_THREAD_H
+
+#ifdef WINDOWS
+
+#include <windows.h>
+#include <thread.h>
+typedef DWORD AnodeThreadId;
+
+#else /* not WINDOWS */
+
+#include <pthread.h>
+typedef pthread_t AnodeThreadId;
+
+#define AnodeThread_self() pthread_self()
+#define AnodeThreadId_equal(a,b) pthread_equal((pthread_t)(a),(pthread_t)(b))
+
+#endif
+
+typedef void AnodeThread;
+
+/**
+ * Create and launch a new thread
+ *
+ * If wait_for_join is true (nonzero), the thread can and must be joined. The
+ * thread object won't be freed until join is called and returns. If
+ * wait_for_join is false, the thread object frees itself automatically on
+ * termination.
+ *
+ * If wait_for_join is false (zero), there is really no need to keep track of
+ * the thread object.
+ *
+ * @param func Function to call as thread main
+ * @param arg Argument to pass to function
+ * @param wait_for_join If false, thread deletes itself when it terminates
+ */
+AnodeThread *AnodeThread_create(void (*func)(void *),void *arg,int wait_for_join);
+
+/**
+ * Wait for a thread to terminate and delete thread object
+ *
+ * This can only be used for threads created with wait_for_join set to true.
+ * The thread object is no longer valid after this call.
+ *
+ * @param thread Thread to wait for termination and delete
+ */
+void AnodeThread_join(AnodeThread *thread);
+
+#endif
diff --git a/attic/historic/anode/libanode/impl/types.h b/attic/historic/anode/libanode/impl/types.h
new file mode 100644
index 00000000..5f070e5a
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/types.h
@@ -0,0 +1,25 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _ANODE_TYPES_H
+#define _ANODE_TYPES_H
+
+#ifdef WINDOWS
+#else
+#include <stdint.h>
+#endif
+
+#endif
diff --git a/attic/historic/anode/libanode/network_address.c b/attic/historic/anode/libanode/network_address.c
new file mode 100644
index 00000000..86ec054f
--- /dev/null
+++ b/attic/historic/anode/libanode/network_address.c
@@ -0,0 +1,136 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "impl/misc.h"
+#include "impl/types.h"
+#include "anode.h"
+
+const AnodeNetworkAddress AnodeNetworkAddress_ANY4 = {
+ ANODE_NETWORK_ADDRESS_IPV4,
+ { 0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }
+};
+const AnodeNetworkAddress AnodeNetworkAddress_ANY6 = {
+ ANODE_NETWORK_ADDRESS_IPV6,
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }
+};
+const AnodeNetworkAddress AnodeNetworkAddress_LOCAL4 = {
+ ANODE_NETWORK_ADDRESS_IPV4,
+ { 127,0,0,1, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }
+};
+const AnodeNetworkAddress AnodeNetworkAddress_LOCAL6 = {
+ ANODE_NETWORK_ADDRESS_IPV6,
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 ,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }
+};
+
+int AnodeNetworkAddress_to_string(const AnodeNetworkAddress *address,char *buf,int len)
+{
+ const char *s;
+
+ switch(address->type) {
+ case ANODE_NETWORK_ADDRESS_IPV4:
+ s = inet_ntop(AF_INET,(const void *)address->bits,buf,len);
+ if (s)
+ return Anode_strlen(s);
+ else return ANODE_ERR_INVALID_ARGUMENT;
+ break;
+ case ANODE_NETWORK_ADDRESS_IPV6:
+ s = inet_ntop(AF_INET6,address->bits,buf,len);
+ if (s)
+ return Anode_strlen(s);
+ else return ANODE_ERR_INVALID_ARGUMENT;
+ /*
+ case ANODE_NETWORK_ADDRESS_ETHERNET:
+ break;
+ case ANODE_NETWORK_ADDRESS_USB:
+ break;
+ case ANODE_NETWORK_ADDRESS_BLUETOOTH:
+ break;
+ case ANODE_NETWORK_ADDRESS_IPC:
+ break;
+ case ANODE_NETWORK_ADDRESS_80211S:
+ break;
+ case ANODE_NETWORK_ADDRESS_SERIAL:
+ break;
+ */
+ case ANODE_NETWORK_ADDRESS_ANODE_256_40:
+ return AnodeAddress_to_string((const AnodeAddress *)address->bits,buf,len);
+ default:
+ return ANODE_ERR_ADDRESS_TYPE_NOT_SUPPORTED;
+ }
+}
+
+int AnodeNetworkAddress_from_string(const char *str,AnodeNetworkAddress *address)
+{
+ unsigned int dots = Anode_count_char(str,'.');
+ unsigned int colons = Anode_count_char(str,':');
+
+ if ((dots == 3)&&(!colons)) {
+ address->type = ANODE_NETWORK_ADDRESS_IPV4;
+ if (inet_pton(AF_INET,str,address->bits) > 0)
+ return 0;
+ else return ANODE_ERR_INVALID_ARGUMENT;
+ } else if ((colons)&&(!dots)) {
+ address->type = ANODE_NETWORK_ADDRESS_IPV6;
+ if (inet_pton(AF_INET6,str,address->bits) > 0)
+ return 0;
+ else return ANODE_ERR_INVALID_ARGUMENT;
+ } else {
+ address->type = ANODE_NETWORK_ADDRESS_ANODE_256_40;
+ return AnodeAddress_from_string(str,(AnodeAddress *)address->bits);
+ }
+}
+
+int AnodeNetworkEndpoint_from_sockaddr(const void *sockaddr,AnodeNetworkEndpoint *endpoint)
+{
+ switch(((struct sockaddr_storage *)sockaddr)->ss_family) {
+ case AF_INET:
+ *((uint32_t *)endpoint->address.bits) = (uint32_t)(((struct sockaddr_in *)sockaddr)->sin_addr.s_addr);
+ endpoint->port = (int)ntohs(((struct sockaddr_in *)sockaddr)->sin_port);
+ return 0;
+ case AF_INET6:
+ Anode_memcpy(endpoint->address.bits,((struct sockaddr_in6 *)sockaddr)->sin6_addr.s6_addr,16);
+ endpoint->port = (int)ntohs(((struct sockaddr_in6 *)sockaddr)->sin6_port);
+ return 0;
+ default:
+ return ANODE_ERR_INVALID_ARGUMENT;
+ }
+}
+
+int AnodeNetworkEndpoint_to_sockaddr(const AnodeNetworkEndpoint *endpoint,void *sockaddr,int sockaddr_len)
+{
+ switch(endpoint->address.type) {
+ case ANODE_NETWORK_ADDRESS_IPV4:
+ if (sockaddr_len < (int)sizeof(struct sockaddr_in))
+ return ANODE_ERR_BUFFER_TOO_SMALL;
+ Anode_zero(sockaddr,sizeof(struct sockaddr_in));
+ ((struct sockaddr_in *)sockaddr)->sin_family = AF_INET;
+ ((struct sockaddr_in *)sockaddr)->sin_port = htons((uint16_t)endpoint->port);
+ ((struct sockaddr_in *)sockaddr)->sin_addr.s_addr = *((uint32_t *)endpoint->address.bits);
+ return 0;
+ case ANODE_NETWORK_ADDRESS_IPV6:
+ if (sockaddr_len < (int)sizeof(struct sockaddr_in6))
+ return ANODE_ERR_BUFFER_TOO_SMALL;
+ Anode_zero(sockaddr,sizeof(struct sockaddr_in6));
+ ((struct sockaddr_in6 *)sockaddr)->sin6_family = AF_INET6;
+ ((struct sockaddr_in6 *)sockaddr)->sin6_port = htons((uint16_t)endpoint->port);
+ Anode_memcpy(((struct sockaddr_in6 *)sockaddr)->sin6_addr.s6_addr,endpoint->address.bits,16);
+ return 0;
+ default:
+ return ANODE_ERR_INVALID_ARGUMENT;
+ }
+}
diff --git a/attic/historic/anode/libanode/secure_random.c b/attic/historic/anode/libanode/secure_random.c
new file mode 100644
index 00000000..4322d7de
--- /dev/null
+++ b/attic/historic/anode/libanode/secure_random.c
@@ -0,0 +1,88 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "impl/aes.h"
+#include "impl/misc.h"
+#include "anode.h"
+
+#ifdef WINDOWS
+#include <windows.h>
+#include <wincrypt.h>
+#endif
+
+struct AnodeSecureRandomImpl
+{
+ AnodeAesExpandedKey key;
+ unsigned char state[ANODE_AES_BLOCK_SIZE];
+ unsigned char block[ANODE_AES_BLOCK_SIZE];
+ unsigned int ptr;
+};
+
+AnodeSecureRandom *AnodeSecureRandom_new()
+{
+ unsigned char keybuf[ANODE_AES_KEY_SIZE + ANODE_AES_BLOCK_SIZE + ANODE_AES_BLOCK_SIZE];
+ unsigned int i;
+ struct AnodeSecureRandomImpl *srng;
+
+#ifdef WINDOWS
+ HCRYPTPROV hProv;
+ if (CryptAcquireContext(&hProv,NULL,NULL,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) {
+ CryptGenRandom(hProv,sizeof(keybuf),keybuf);
+ CryptReleaseContext(hProv,0);
+ }
+#else
+ FILE *urandf = fopen("/dev/urandom","rb");
+ if (urandf) {
+ fread((void *)keybuf,sizeof(keybuf),1,urandf);
+ fclose(urandf);
+ }
+#endif
+
+ for(i=0;i<sizeof(keybuf);++i)
+ keybuf[i] ^= (unsigned char)(Anode_rand() >> 5);
+
+ srng = malloc(sizeof(struct AnodeSecureRandomImpl));
+ Anode_aes256_expand_key(keybuf,&srng->key);
+ for(i=0;i<ANODE_AES_BLOCK_SIZE;++i)
+ srng->state[i] = keybuf[ANODE_AES_KEY_SIZE + i];
+ for(i=0;i<ANODE_AES_BLOCK_SIZE;++i)
+ srng->block[i] = keybuf[ANODE_AES_KEY_SIZE + ANODE_AES_KEY_SIZE + i];
+ srng->ptr = ANODE_AES_BLOCK_SIZE;
+
+ return (AnodeSecureRandom *)srng;
+}
+
+void AnodeSecureRandom_gen_bytes(AnodeSecureRandom *srng,void *buf,long count)
+{
+ long i,j;
+
+ for(i=0;i<count;++i) {
+ if (((struct AnodeSecureRandomImpl *)srng)->ptr == ANODE_AES_BLOCK_SIZE) {
+ Anode_aes256_encrypt(&((struct AnodeSecureRandomImpl *)srng)->key,((struct AnodeSecureRandomImpl *)srng)->state,((struct AnodeSecureRandomImpl *)srng)->state);
+ for(j=0;j<ANODE_AES_KEY_SIZE;++j)
+ ((struct AnodeSecureRandomImpl *)srng)->block[j] ^= ((struct AnodeSecureRandomImpl *)srng)->state[j];
+ ((struct AnodeSecureRandomImpl *)srng)->ptr = 0;
+ }
+ ((unsigned char *)buf)[i] = ((struct AnodeSecureRandomImpl *)srng)->block[((struct AnodeSecureRandomImpl *)srng)->ptr++];
+ }
+}
+
+void AnodeSecureRandom_delete(AnodeSecureRandom *srng)
+{
+ free(srng);
+}
diff --git a/attic/historic/anode/libanode/system_transport.c b/attic/historic/anode/libanode/system_transport.c
new file mode 100644
index 00000000..4bfb143e
--- /dev/null
+++ b/attic/historic/anode/libanode/system_transport.c
@@ -0,0 +1,948 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include "anode.h"
+#include "impl/mutex.h"
+#include "impl/thread.h"
+#include "impl/misc.h"
+#include "impl/dns_txt.h"
+
+#ifdef WINDOWS
+#include <windows.h>
+#include <winsock2.h>
+#define AnodeSystemTransport__close_socket(s) closesocket((s))
+#define ANODE_USE_SELECT 1
+#else
+#include <poll.h>
+#include <unistd.h>
+#define AnodeSystemTransport__close_socket(s) close((s))
+#endif
+
+static const char *AnodeSystemTransport_CLASS = "SystemTransport";
+
+/* ======================================================================== */
+
+struct AnodeSystemTransport;
+
+struct AnodeSystemTransport_AnodeSocket
+{
+ AnodeSocket base; /* must be first */
+ unsigned int entry_idx;
+};
+
+#define ANODE_SYSTEM_TRANSPORT_DNS_MAX_RESULTS 16
+struct AnodeSystemTransport__dns_request
+{
+ struct AnodeSystemTransport__dns_request *next;
+
+ AnodeThread *thread;
+ struct AnodeSystemTransport *owner;
+
+ void (*event_handler)(const AnodeEvent *event);
+
+ char name[256];
+ enum AnodeTransportDnsIncludeMode ipv4_include_mode;
+ enum AnodeTransportDnsIncludeMode ipv6_include_mode;
+ enum AnodeTransportDnsIncludeMode anode_include_mode;
+
+ AnodeNetworkAddress addresses[ANODE_SYSTEM_TRANSPORT_DNS_MAX_RESULTS];
+ unsigned int address_count;
+
+ int error_code;
+};
+
+#ifdef ANODE_USE_SELECT
+typedef int AnodeSystemTransport__poll_fd; /* for select() */
+#else
+typedef struct pollfd AnodeSystemTransport__poll_fd; /* for poll() */
+#endif
+
+struct AnodeSystemTransport
+{
+ AnodeTransport interface; /* must be first */
+
+ AnodeTransport *base;
+
+#ifdef ANODE_USE_SELECT
+ FD_SET readfds;
+ FD_SET writefds;
+#endif
+
+ void (*default_event_handler)(const AnodeEvent *event);
+
+ AnodeSystemTransport__poll_fd *fds;
+ struct AnodeSystemTransport_AnodeSocket *sockets;
+ unsigned int fd_count;
+ unsigned int fd_capacity;
+
+ struct AnodeSystemTransport__dns_request *pending_dns_requests;
+
+ int invoke_pipe[2];
+ AnodeMutex invoke_pipe_m;
+ void *invoke_pipe_buf[2];
+ unsigned int invoke_pipe_buf_ptr;
+};
+
+/* ======================================================================== */
+/* Internal helper methods */
+
+static unsigned int AnodeSystemTransport__add_entry(struct AnodeSystemTransport *transport)
+{
+ if ((transport->fd_count + 1) > transport->fd_capacity) {
+ transport->fd_capacity += 8;
+ transport->fds = realloc(transport->fds,sizeof(AnodeSystemTransport__poll_fd) * transport->fd_capacity);
+ transport->sockets = realloc(transport->sockets,sizeof(struct AnodeSystemTransport_AnodeSocket) * transport->fd_capacity);
+ }
+ return transport->fd_count++;
+}
+
+static void AnodeSystemTransport__remove_entry(struct AnodeSystemTransport *transport,const unsigned int idx)
+{
+ unsigned int i;
+
+ --transport->fd_count;
+ for(i=idx;i<transport->fd_count;++i) {
+ Anode_memcpy(&transport->fds[i],&transport->fds[i+1],sizeof(AnodeSystemTransport__poll_fd));
+ Anode_memcpy(&transport->sockets[i],&transport->sockets[i+1],sizeof(struct AnodeSystemTransport_AnodeSocket));
+ }
+
+ if ((transport->fd_capacity - transport->fd_count) > 16) {
+ transport->fd_capacity -= 16;
+ transport->fds = realloc(transport->fds,sizeof(AnodeSystemTransport__poll_fd) * transport->fd_capacity);
+ transport->sockets = realloc(transport->sockets,sizeof(struct AnodeSystemTransport_AnodeSocket) * transport->fd_capacity);
+ }
+}
+
+static void AnodeSystemTransport__dns_invoke_on_completion(void *_dreq)
+{
+ struct AnodeSystemTransport__dns_request *dreq = (struct AnodeSystemTransport__dns_request *)_dreq;
+ struct AnodeSystemTransport__dns_request *ptr,**lastnext;
+
+ AnodeThread_join(dreq->thread);
+
+ ptr = dreq->owner->pending_dns_requests;
+ lastnext = &dreq->owner->pending_dns_requests;
+ while (ptr) {
+ if (ptr == dreq) {
+ *lastnext = ptr->next;
+ break;
+ } else {
+ lastnext = &ptr->next;
+ ptr = ptr->next;
+ }
+ }
+
+ free(dreq);
+}
+
+static void AnodeSystemTransport__dns_thread_main(void *_dreq)
+{
+ struct AnodeSystemTransport__dns_request *dreq = (struct AnodeSystemTransport__dns_request *)_dreq;
+
+ dreq->owner->interface.invoke((AnodeTransport *)dreq->owner,dreq,&AnodeSystemTransport__dns_invoke_on_completion);
+}
+
+static void AnodeSystemTransport__do_close(struct AnodeSystemTransport *transport,struct AnodeSystemTransport_AnodeSocket *sock,const int error_code,const int generate_event)
+{
+ AnodeEvent evbuf;
+ int fd;
+
+ if (sock->base.class_name == AnodeSystemTransport_CLASS) {
+#ifdef ANODE_USE_SELECT
+ fd = (int)(transport->fds[((struct AnodeSystemTransport_AnodeSocket *)sock)->entry_idx]);
+#else
+ fd = transport->fds[((struct AnodeSystemTransport_AnodeSocket *)sock)->entry_idx].fd;
+#endif
+
+ if ((sock->base.type == ANODE_SOCKET_STREAM_CONNECTION)&&(sock->base.state != ANODE_SOCKET_CLOSED)) {
+ sock->base.state = ANODE_SOCKET_CLOSED;
+
+ if (generate_event) {
+ evbuf.type = ANODE_TRANSPORT_EVENT_STREAM_CLOSED;
+ evbuf.transport = (AnodeTransport *)transport;
+ evbuf.sock = (AnodeSocket *)sock;
+ evbuf.datagram_from = NULL;
+ evbuf.dns_name = NULL;
+ evbuf.dns_addresses = NULL;
+ evbuf.dns_address_count = 0;
+ evbuf.error_code = error_code;
+ evbuf.data_length = 0;
+ evbuf.data = NULL;
+
+ if (sock->base.event_handler)
+ sock->base.event_handler(&evbuf);
+ else if (transport->default_event_handler)
+ transport->default_event_handler(&evbuf);
+ }
+ }
+
+ AnodeSystemTransport__close_socket(fd);
+ AnodeSystemTransport__remove_entry(transport,((struct AnodeSystemTransport_AnodeSocket *)sock)->entry_idx);
+
+#ifdef ANODE_USE_SELECT
+ FD_CLR(sock,&THIS->readfds);
+ FD_CLR(sock,&THIS->writefds);
+#endif
+ } else transport->base->close(transport->base,(AnodeSocket *)sock);
+}
+
+static int AnodeSystemTransport__populate_network_endpoint(const struct sockaddr_storage *saddr,AnodeNetworkEndpoint *ep)
+{
+ switch(saddr->ss_family) {
+ case AF_INET:
+ ep->address.type = ANODE_NETWORK_ADDRESS_IPV4;
+ *((uint32_t *)ep->address.bits) = ((struct sockaddr_in *)saddr)->sin_addr.s_addr;
+ ep->port = ntohs(((struct sockaddr_in *)saddr)->sin_port);
+ return 1;
+ case AF_INET6:
+ ep->address.type = ANODE_NETWORK_ADDRESS_IPV6;
+ Anode_memcpy(ep->address.bits,((struct sockaddr_in6 *)saddr)->sin6_addr.s6_addr,16);
+ ep->port = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
+ return 1;
+ }
+ return 0;
+}
+
+/* ======================================================================== */
+
+#ifdef THIS
+#undef THIS
+#endif
+#define THIS ((struct AnodeSystemTransport *)transport)
+
+static void AnodeSystemTransport_invoke(AnodeTransport *transport,
+ void *ptr,
+ void (*func)(void *))
+{
+ void *invoke_msg[2];
+
+ invoke_msg[0] = ptr;
+ invoke_msg[1] = (void *)func;
+
+ AnodeMutex_lock(&THIS->invoke_pipe_m);
+ write(THIS->invoke_pipe[1],(void *)(&invoke_msg),sizeof(invoke_msg));
+ AnodeMutex_unlock(&THIS->invoke_pipe_m);
+}
+
+static void AnodeSystemTransport_dns_resolve(AnodeTransport *transport,
+ const char *name,
+ void (*event_handler)(const AnodeEvent *),
+ enum AnodeTransportDnsIncludeMode ipv4_include_mode,
+ enum AnodeTransportDnsIncludeMode ipv6_include_mode,
+ enum AnodeTransportDnsIncludeMode anode_include_mode)
+{
+ struct AnodeSystemTransport__dns_request *dreq = malloc(sizeof(struct AnodeSystemTransport__dns_request));
+
+ dreq->owner = THIS;
+ dreq->event_handler = event_handler;
+ Anode_str_copy(dreq->name,name,sizeof(dreq->name));
+ dreq->ipv4_include_mode = ipv4_include_mode;
+ dreq->ipv6_include_mode = ipv6_include_mode;
+ dreq->anode_include_mode = anode_include_mode;
+
+ dreq->address_count = 0;
+ dreq->error_code = 0;
+
+ dreq->next = THIS->pending_dns_requests;
+ THIS->pending_dns_requests = dreq;
+
+ dreq->thread = AnodeThread_create(&AnodeSystemTransport__dns_thread_main,dreq,0);
+}
+
+static AnodeSocket *AnodeSystemTransport_datagram_listen(AnodeTransport *transport,
+ const AnodeNetworkAddress *local_address,
+ int local_port,
+ int *error_code)
+{
+ struct sockaddr_in sin4;
+ struct sockaddr_in6 sin6;
+ struct AnodeSystemTransport_AnodeSocket *sock;
+ unsigned int entry_idx;
+ int fd;
+ int tmp;
+
+ switch(local_address->type) {
+ case ANODE_NETWORK_ADDRESS_IPV4:
+ fd = socket(AF_INET,SOCK_DGRAM,0);
+ if (fd <= 0) {
+ *error_code = ANODE_ERR_UNABLE_TO_BIND;
+ return (AnodeSocket *)0;
+ }
+ tmp = 1;
+ setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&tmp,sizeof(tmp));
+ fcntl(fd,F_SETFL,O_NONBLOCK);
+
+ Anode_zero(&sin4,sizeof(struct sockaddr_in));
+ sin4.sin_family = AF_INET;
+ sin4.sin_port = htons(local_port);
+ sin4.sin_addr.s_addr = *((uint32_t *)local_address->bits);
+
+ if (bind(fd,(const struct sockaddr *)&sin4,sizeof(sin4))) {
+ AnodeSystemTransport__close_socket(fd);
+ *error_code = ANODE_ERR_UNABLE_TO_BIND;
+ return (AnodeSocket *)0;
+ }
+ break;
+ case ANODE_NETWORK_ADDRESS_IPV6:
+ fd = socket(AF_INET6,SOCK_DGRAM,0);
+ if (fd <= 0) {
+ *error_code = ANODE_ERR_UNABLE_TO_BIND;
+ return (AnodeSocket *)0;
+ }
+ tmp = 1; setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&tmp,sizeof(tmp));
+ fcntl(fd,F_SETFL,O_NONBLOCK);
+#ifdef IPV6_V6ONLY
+ tmp = 1; setsockopt(fd,IPPROTO_IPV6,IPV6_V6ONLY,&tmp,sizeof(tmp));
+#endif
+
+ Anode_zero(&sin6,sizeof(struct sockaddr_in6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(local_port);
+ Anode_memcpy(sin6.sin6_addr.s6_addr,local_address->bits,16);
+
+ if (bind(fd,(const struct sockaddr *)&sin6,sizeof(sin6))) {
+ AnodeSystemTransport__close_socket(fd);
+ *error_code = ANODE_ERR_UNABLE_TO_BIND;
+ return (AnodeSocket *)0;
+ }
+ break;
+ default:
+ if (THIS->base)
+ return THIS->base->datagram_listen(THIS->base,local_address,local_port,error_code);
+ else {
+ *error_code = ANODE_ERR_ADDRESS_TYPE_NOT_SUPPORTED;
+ return (AnodeSocket *)0;
+ }
+ }
+
+ entry_idx = AnodeSystemTransport__add_entry(THIS);
+ sock = &(THIS->sockets[entry_idx]);
+
+ sock->base.type = ANODE_SOCKET_DATAGRAM;
+ sock->base.state = ANODE_SOCKET_OPEN;
+ Anode_memcpy(&sock->base.endpoint.address,local_address,sizeof(AnodeNetworkAddress));
+ sock->base.endpoint.port = local_port;
+ sock->base.class_name = AnodeSystemTransport_CLASS;
+ sock->base.user_ptr[0] = NULL;
+ sock->base.user_ptr[1] = NULL;
+ sock->base.event_handler = NULL;
+ sock->entry_idx = entry_idx;
+
+ THIS->fds[entry_idx].fd = fd;
+ THIS->fds[entry_idx].events = POLLIN;
+ THIS->fds[entry_idx].revents = 0;
+
+ *error_code = 0;
+ return (AnodeSocket *)sock;
+}
+
+static AnodeSocket *AnodeSystemTransport_stream_listen(AnodeTransport *transport,
+ const AnodeNetworkAddress *local_address,
+ int local_port,
+ int *error_code)
+{
+ struct sockaddr_in sin4;
+ struct sockaddr_in6 sin6;
+ struct AnodeSystemTransport_AnodeSocket *sock;
+ unsigned int entry_idx;
+ int fd;
+ int tmp;
+
+ switch(local_address->type) {
+ case ANODE_NETWORK_ADDRESS_IPV4:
+ fd = socket(AF_INET,SOCK_STREAM,0);
+ if (fd < 0) {
+ *error_code = ANODE_ERR_UNABLE_TO_BIND;
+ return (AnodeSocket *)0;
+ }
+ fcntl(fd,F_SETFL,O_NONBLOCK);
+
+ Anode_zero(&sin4,sizeof(struct sockaddr_in));
+ sin4.sin_family = AF_INET;
+ sin4.sin_port = htons(local_port);
+ sin4.sin_addr.s_addr = *((uint32_t *)local_address->bits);
+
+ if (bind(fd,(const struct sockaddr *)&sin4,sizeof(sin4))) {
+ AnodeSystemTransport__close_socket(fd);
+ *error_code = ANODE_ERR_UNABLE_TO_BIND;
+ return (AnodeSocket *)0;
+ }
+ if (listen(fd,8)) {
+ AnodeSystemTransport__close_socket(fd);
+ *error_code = ANODE_ERR_UNABLE_TO_BIND;
+ return (AnodeSocket *)0;
+ }
+ break;
+ case ANODE_NETWORK_ADDRESS_IPV6:
+ fd = socket(AF_INET6,SOCK_STREAM,0);
+ if (fd < 0) {
+ *error_code = ANODE_ERR_UNABLE_TO_BIND;
+ return (AnodeSocket *)0;
+ }
+ fcntl(fd,F_SETFL,O_NONBLOCK);
+#ifdef IPV6_V6ONLY
+ tmp = 1; setsockopt(fd,IPPROTO_IPV6,IPV6_V6ONLY,&tmp,sizeof(tmp));
+#endif
+
+ Anode_zero(&sin6,sizeof(struct sockaddr_in6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(local_port);
+ Anode_memcpy(sin6.sin6_addr.s6_addr,local_address->bits,16);
+
+ if (bind(fd,(const struct sockaddr *)&sin6,sizeof(sin6))) {
+ AnodeSystemTransport__close_socket(fd);
+ *error_code = ANODE_ERR_UNABLE_TO_BIND;
+ return (AnodeSocket *)0;
+ }
+ if (listen(fd,8)) {
+ AnodeSystemTransport__close_socket(fd);
+ *error_code = ANODE_ERR_UNABLE_TO_BIND;
+ return (AnodeSocket *)0;
+ }
+ break;
+ default:
+ if (THIS->base)
+ return THIS->base->stream_listen(THIS->base,local_address,local_port,error_code);
+ else {
+ *error_code = ANODE_ERR_ADDRESS_TYPE_NOT_SUPPORTED;
+ return (AnodeSocket *)0;
+ }
+ }
+
+ entry_idx = AnodeSystemTransport__add_entry(THIS);
+ sock = &(THIS->sockets[entry_idx]);
+
+ sock->base.type = ANODE_SOCKET_STREAM_LISTEN;
+ sock->base.state = ANODE_SOCKET_OPEN;
+ Anode_memcpy(&sock->base.endpoint.address,local_address,sizeof(AnodeNetworkAddress));
+ sock->base.endpoint.port = local_port;
+ sock->base.class_name = AnodeSystemTransport_CLASS;
+ sock->base.user_ptr[0] = NULL;
+ sock->base.user_ptr[1] = NULL;
+ sock->base.event_handler = NULL;
+ sock->entry_idx = entry_idx;
+
+ THIS->fds[entry_idx].fd = fd;
+ THIS->fds[entry_idx].events = POLLIN;
+ THIS->fds[entry_idx].revents = 0;
+
+ *error_code = 0;
+ return (AnodeSocket *)sock;
+}
+
+static int AnodeSystemTransport_datagram_send(AnodeTransport *transport,
+ AnodeSocket *sock,
+ const void *data,
+ int data_len,
+ const AnodeNetworkEndpoint *to_endpoint)
+{
+ struct sockaddr_in sin4;
+ struct sockaddr_in6 sin6;
+
+#ifdef ANODE_USE_SELECT
+ const int fd = (int)(THIS->fds[((struct AnodeSystemTransport_AnodeSocket *)sock)->entry_idx]);
+#else
+ const int fd = THIS->fds[((struct AnodeSystemTransport_AnodeSocket *)sock)->entry_idx].fd;
+#endif
+
+ switch(to_endpoint->address.type) {
+ case ANODE_NETWORK_ADDRESS_IPV4:
+ Anode_zero(&sin4,sizeof(struct sockaddr_in));
+ sin4.sin_family = AF_INET;
+ sin4.sin_port = htons((uint16_t)to_endpoint->port);
+ sin4.sin_addr.s_addr = *((uint32_t *)to_endpoint->address.bits);
+ sendto(fd,data,data_len,0,(struct sockaddr *)&sin4,sizeof(sin4));
+ return 0;
+ case ANODE_NETWORK_ADDRESS_IPV6:
+ Anode_zero(&sin6,sizeof(struct sockaddr_in6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons((uint16_t)to_endpoint->port);
+ Anode_memcpy(sin6.sin6_addr.s6_addr,to_endpoint->address.bits,16);
+ sendto(fd,data,data_len,0,(struct sockaddr *)&sin6,sizeof(sin6));
+ return 0;
+ default:
+ if (THIS->base)
+ return THIS->base->datagram_send(THIS->base,sock,data,data_len,to_endpoint);
+ else return ANODE_ERR_ADDRESS_TYPE_NOT_SUPPORTED;
+ }
+}
+
+static AnodeSocket *AnodeSystemTransport_stream_connect(AnodeTransport *transport,
+ const AnodeNetworkEndpoint *to_endpoint,
+ int *error_code)
+{
+ struct sockaddr_in sin4;
+ struct sockaddr_in6 sin6;
+ struct AnodeSystemTransport_AnodeSocket *sock;
+ unsigned int entry_idx;
+ int fd;
+
+ switch(to_endpoint->address.type) {
+ case ANODE_NETWORK_ADDRESS_IPV4:
+ Anode_zero(&sin4,sizeof(struct sockaddr_in));
+ sin4.sin_family = AF_INET;
+ sin4.sin_port = htons(to_endpoint->port);
+ sin4.sin_addr.s_addr = *((uint32_t *)to_endpoint->address.bits);
+
+ fd = socket(AF_INET,SOCK_STREAM,0);
+ if (fd < 0) {
+ *error_code = ANODE_ERR_ADDRESS_TYPE_NOT_SUPPORTED;
+ return (AnodeSocket *)0;
+ }
+ fcntl(fd,F_SETFL,O_NONBLOCK);
+
+ if (connect(fd,(struct sockaddr *)&sin4,sizeof(sin4))) {
+ if (errno != EINPROGRESS) {
+ *error_code = ANODE_ERR_CONNECT_FAILED;
+ AnodeSystemTransport__close_socket(fd);
+ return (AnodeSocket *)0;
+ }
+ }
+ break;
+ case ANODE_NETWORK_ADDRESS_IPV6:
+ Anode_zero(&sin6,sizeof(struct sockaddr_in6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(to_endpoint->port);
+ Anode_memcpy(sin6.sin6_addr.s6_addr,to_endpoint->address.bits,16);
+
+ fd = socket(AF_INET6,SOCK_STREAM,0);
+ if (fd < 0) {
+ *error_code = ANODE_ERR_ADDRESS_TYPE_NOT_SUPPORTED;
+ return (AnodeSocket *)0;
+ }
+ fcntl(fd,F_SETFL,O_NONBLOCK);
+
+ if (connect(fd,(struct sockaddr *)&sin6,sizeof(sin6))) {
+ if (errno == EINPROGRESS) {
+ *error_code = ANODE_ERR_CONNECT_FAILED;
+ AnodeSystemTransport__close_socket(fd);
+ return (AnodeSocket *)0;
+ }
+ }
+ break;
+ default:
+ if (THIS->base)
+ return THIS->base->stream_connect(THIS->base,to_endpoint,error_code);
+ else {
+ *error_code = ANODE_ERR_ADDRESS_TYPE_NOT_SUPPORTED;
+ return (AnodeSocket *)0;
+ }
+ }
+
+ entry_idx = AnodeSystemTransport__add_entry(THIS);
+ sock = &(THIS->sockets[entry_idx]);
+
+ sock->base.type = ANODE_SOCKET_STREAM_CONNECTION;
+ sock->base.state = ANODE_SOCKET_CONNECTING;
+ Anode_memcpy(&sock->base.endpoint,to_endpoint,sizeof(AnodeNetworkEndpoint));
+ sock->base.class_name = AnodeSystemTransport_CLASS;
+ sock->base.user_ptr[0] = NULL;
+ sock->base.user_ptr[1] = NULL;
+ sock->base.event_handler = NULL;
+ sock->entry_idx = entry_idx;
+
+ THIS->fds[entry_idx].fd = fd;
+ THIS->fds[entry_idx].events = POLLIN|POLLOUT;
+ THIS->fds[entry_idx].revents = 0;
+
+ return (AnodeSocket *)sock;
+}
+
+static void AnodeSystemTransport_stream_start_writing(AnodeTransport *transport,
+ AnodeSocket *sock)
+{
+ if ((sock->type == ANODE_SOCKET_STREAM_CONNECTION)&&(((struct AnodeSystemTransport_AnodeSocket *)sock)->base.state == ANODE_SOCKET_OPEN)) {
+ if (sock->class_name == AnodeSystemTransport_CLASS) {
+#ifdef ANODE_USE_SELECT
+ FD_SET((int)(THIS->fds[((struct AnodeSystemTransport_AnodeSocket *)sock)->entry_idx]),&THIS->writefds);
+#else
+ THIS->fds[((struct AnodeSystemTransport_AnodeSocket *)sock)->entry_idx].events = (POLLIN|POLLOUT);
+#endif
+ } else THIS->base->stream_start_writing(THIS->base,sock);
+ }
+}
+
+static void AnodeSystemTransport_stream_stop_writing(AnodeTransport *transport,
+ AnodeSocket *sock)
+{
+ if ((sock->type == ANODE_SOCKET_STREAM_CONNECTION)&&(((struct AnodeSystemTransport_AnodeSocket *)sock)->base.state == ANODE_SOCKET_OPEN)) {
+ if (sock->class_name == AnodeSystemTransport_CLASS) {
+#ifdef ANODE_USE_SELECT
+ FD_CLR((int)(THIS->fds[((struct AnodeSystemTransport_AnodeSocket *)sock)->entry_idx]),&THIS->writefds);
+#else
+ THIS->fds[((struct AnodeSystemTransport_AnodeSocket *)sock)->entry_idx].events = POLLIN;
+#endif
+ } else THIS->base->stream_stop_writing(THIS->base,sock);
+ }
+}
+
+static int AnodeSystemTransport_stream_send(AnodeTransport *transport,
+ AnodeSocket *sock,
+ const void *data,
+ int data_len)
+{
+ int result;
+
+ if (sock->type == ANODE_SOCKET_STREAM_CONNECTION) {
+ if (sock->class_name == AnodeSystemTransport_CLASS) {
+ if (((struct AnodeSystemTransport_AnodeSocket *)sock)->base.state != ANODE_SOCKET_OPEN)
+ return ANODE_ERR_CONNECTION_CLOSED;
+
+#ifdef ANODE_USE_SELECT
+ result = send((int)(THIS->fds[((struct AnodeSystemTransport_AnodeSocket *)sock)->entry_idx]),data,data_len,0);
+#else
+ result = send(THIS->fds[((struct AnodeSystemTransport_AnodeSocket *)sock)->entry_idx].fd,data,data_len,0);
+#endif
+
+ if (result >= 0)
+ return result;
+ else {
+ AnodeSystemTransport__do_close(THIS,(struct AnodeSystemTransport_AnodeSocket *)sock,ANODE_ERR_CONNECTION_CLOSED_BY_REMOTE,1);
+ return ANODE_ERR_CONNECTION_CLOSED;
+ }
+ } else return THIS->base->stream_send(THIS->base,sock,data,data_len);
+ } else return ANODE_ERR_INVALID_ARGUMENT;
+}
+
+static void AnodeSystemTransport_close(AnodeTransport *transport,
+ AnodeSocket *sock)
+{
+ AnodeSystemTransport__do_close(THIS,(struct AnodeSystemTransport_AnodeSocket *)sock,0,1);
+}
+
+static void AnodeSystemTransport__poll_do_read_datagram(struct AnodeSystemTransport *transport,int fd,struct AnodeSystemTransport_AnodeSocket *sock)
+{
+ char buf[16384];
+ struct sockaddr_storage fromaddr;
+ AnodeNetworkEndpoint tmp_ep;
+ AnodeEvent evbuf;
+ socklen_t addrlen;
+ int n;
+
+ addrlen = sizeof(struct sockaddr_storage);
+ n = recvfrom(fd,buf,sizeof(buf),0,(struct sockaddr *)&fromaddr,&addrlen);
+ if ((n >= 0)&&(AnodeSystemTransport__populate_network_endpoint(&fromaddr,&tmp_ep))) {
+ evbuf.type = ANODE_TRANSPORT_EVENT_DATAGRAM_RECEIVED;
+ evbuf.transport = (AnodeTransport *)transport;
+ evbuf.sock = (AnodeSocket *)sock;
+ evbuf.datagram_from = &tmp_ep;
+ evbuf.dns_name = NULL;
+ evbuf.dns_addresses = NULL;
+ evbuf.dns_address_count = 0;
+ evbuf.error_code = 0;
+ evbuf.data_length = n;
+ evbuf.data = buf;
+
+ if (sock->base.event_handler)
+ sock->base.event_handler(&evbuf);
+ else if (transport->default_event_handler)
+ transport->default_event_handler(&evbuf);
+ }
+}
+
+static void AnodeSystemTransport__poll_do_accept_incoming_connection(struct AnodeSystemTransport *transport,int fd,struct AnodeSystemTransport_AnodeSocket *sock)
+{
+ struct sockaddr_storage fromaddr;
+ AnodeNetworkEndpoint tmp_ep;
+ AnodeEvent evbuf;
+ struct AnodeSystemTransport_AnodeSocket *newsock;
+ socklen_t addrlen;
+ int n;
+ unsigned int entry_idx;
+
+ addrlen = sizeof(struct sockaddr_storage);
+ n = accept(fd,(struct sockaddr *)&fromaddr,&addrlen);
+ if ((n >= 0)&&(AnodeSystemTransport__populate_network_endpoint(&fromaddr,&tmp_ep))) {
+ entry_idx = AnodeSystemTransport__add_entry(transport);
+ newsock = &(transport->sockets[entry_idx]);
+
+ newsock->base.type = ANODE_SOCKET_STREAM_CONNECTION;
+ newsock->base.state = ANODE_SOCKET_OPEN;
+ Anode_memcpy(&newsock->base.endpoint,&tmp_ep,sizeof(AnodeNetworkEndpoint));
+ newsock->base.class_name = AnodeSystemTransport_CLASS;
+ newsock->base.user_ptr[0] = NULL;
+ newsock->base.user_ptr[1] = NULL;
+ newsock->base.event_handler = NULL;
+ newsock->entry_idx = entry_idx;
+
+ THIS->fds[entry_idx].fd = n;
+ THIS->fds[entry_idx].events = POLLIN;
+ THIS->fds[entry_idx].revents = 0;
+
+ evbuf.type = ANODE_TRANSPORT_EVENT_STREAM_INCOMING_CONNECT;
+ evbuf.transport = (AnodeTransport *)transport;
+ evbuf.sock = (AnodeSocket *)newsock;
+ evbuf.datagram_from = NULL;
+ evbuf.dns_name = NULL;
+ evbuf.dns_addresses = NULL;
+ evbuf.dns_address_count = 0;
+ evbuf.error_code = 0;
+ evbuf.data_length = 0;
+ evbuf.data = NULL;
+
+ if (sock->base.event_handler)
+ sock->base.event_handler(&evbuf);
+ else if (transport->default_event_handler)
+ transport->default_event_handler(&evbuf);
+ }
+}
+
+static void AnodeSystemTransport__poll_do_read_stream(struct AnodeSystemTransport *transport,int fd,struct AnodeSystemTransport_AnodeSocket *sock)
+{
+ char buf[65536];
+ AnodeEvent evbuf;
+ int n;
+
+ n = recv(fd,buf,sizeof(buf),0);
+ if (n > 0) {
+ evbuf.type = ANODE_TRANSPORT_EVENT_STREAM_DATA_RECEIVED;
+ evbuf.transport = (AnodeTransport *)transport;
+ evbuf.sock = (AnodeSocket *)sock;
+ evbuf.datagram_from = NULL;
+ evbuf.dns_name = NULL;
+ evbuf.dns_addresses = NULL;
+ evbuf.dns_address_count = 0;
+ evbuf.error_code = 0;
+ evbuf.data_length = n;
+ evbuf.data = buf;
+
+ if (sock->base.event_handler)
+ sock->base.event_handler(&evbuf);
+ else if (transport->default_event_handler)
+ transport->default_event_handler(&evbuf);
+ } else AnodeSystemTransport__do_close(transport,sock,ANODE_ERR_CONNECTION_CLOSED_BY_REMOTE,1);
+}
+
+static void AnodeSystemTransport__poll_do_stream_available_for_write(struct AnodeSystemTransport *transport,int fd,struct AnodeSystemTransport_AnodeSocket *sock)
+{
+ AnodeEvent evbuf;
+
+ evbuf.type = ANODE_TRANSPORT_EVENT_STREAM_DATA_RECEIVED;
+ evbuf.transport = (AnodeTransport *)transport;
+ evbuf.sock = (AnodeSocket *)sock;
+ evbuf.datagram_from = NULL;
+ evbuf.dns_name = NULL;
+ evbuf.dns_addresses = NULL;
+ evbuf.dns_address_count = 0;
+ evbuf.error_code = 0;
+ evbuf.data_length = 0;
+ evbuf.data = NULL;
+
+ if (sock->base.event_handler)
+ sock->base.event_handler(&evbuf);
+ else if (transport->default_event_handler)
+ transport->default_event_handler(&evbuf);
+}
+
+static void AnodeSystemTransport__poll_do_outgoing_connect(struct AnodeSystemTransport *transport,int fd,struct AnodeSystemTransport_AnodeSocket *sock)
+{
+ AnodeEvent evbuf;
+ int err_code;
+ socklen_t optlen;
+
+ optlen = sizeof(err_code);
+ if (getsockopt(fd,SOL_SOCKET,SO_ERROR,(void *)&err_code,&optlen)) {
+ /* Error getting result, so we assume a failure */
+ evbuf.type = ANODE_TRANSPORT_EVENT_STREAM_OUTGOING_CONNECT_FAILED;
+ evbuf.transport = (AnodeTransport *)transport;
+ evbuf.sock = (AnodeSocket *)sock;
+ evbuf.datagram_from = NULL;
+ evbuf.dns_name = NULL;
+ evbuf.dns_addresses = NULL;
+ evbuf.dns_address_count = 0;
+ evbuf.error_code = ANODE_ERR_CONNECT_FAILED;
+ evbuf.data_length = 0;
+ evbuf.data = NULL;
+
+ AnodeSystemTransport__do_close(transport,sock,0,0);
+ } else if (err_code) {
+ /* Error code is nonzero, so connect failed */
+ evbuf.type = ANODE_TRANSPORT_EVENT_STREAM_OUTGOING_CONNECT_FAILED;
+ evbuf.transport = (AnodeTransport *)transport;
+ evbuf.sock = (AnodeSocket *)sock;
+ evbuf.datagram_from = NULL;
+ evbuf.dns_name = NULL;
+ evbuf.dns_addresses = NULL;
+ evbuf.dns_address_count = 0;
+ evbuf.error_code = ANODE_ERR_CONNECT_FAILED;
+ evbuf.data_length = 0;
+ evbuf.data = NULL;
+
+ AnodeSystemTransport__do_close(transport,sock,0,0);
+ } else {
+ /* Connect succeeded */
+ evbuf.type = ANODE_TRANSPORT_EVENT_STREAM_OUTGOING_CONNECT_ESTABLISHED;
+ evbuf.transport = (AnodeTransport *)transport;
+ evbuf.sock = (AnodeSocket *)sock;
+ evbuf.datagram_from = NULL;
+ evbuf.dns_name = NULL;
+ evbuf.dns_addresses = NULL;
+ evbuf.dns_address_count = 0;
+ evbuf.error_code = 0;
+ evbuf.data_length = 0;
+ evbuf.data = NULL;
+ }
+
+ if (sock->base.event_handler)
+ sock->base.event_handler(&evbuf);
+ else if (transport->default_event_handler)
+ transport->default_event_handler(&evbuf);
+}
+
+static int AnodeSystemTransport_poll(AnodeTransport *transport)
+{
+ int timeout = -1;
+ unsigned int fd_idx;
+ int event_count = 0;
+ int n;
+
+ if (poll((struct pollfd *)THIS->fds,THIS->fd_count,timeout) > 0) {
+ for(fd_idx=0;fd_idx<THIS->fd_count;++fd_idx) {
+ if ((THIS->fds[fd_idx].revents & (POLLERR|POLLHUP|POLLNVAL))) {
+ if (THIS->sockets[fd_idx].base.type == ANODE_SOCKET_STREAM_CONNECTION) {
+ if (THIS->sockets[fd_idx].base.state == ANODE_SOCKET_CONNECTING)
+ AnodeSystemTransport__poll_do_outgoing_connect(THIS,THIS->fds[fd_idx].fd,&THIS->sockets[fd_idx]);
+ else AnodeSystemTransport__do_close(THIS,&THIS->sockets[fd_idx],ANODE_ERR_CONNECTION_CLOSED_BY_REMOTE,1);
+ ++event_count;
+ }
+ } else {
+ if ((THIS->fds[fd_idx].revents & POLLIN)) {
+ if (THIS->fds[fd_idx].fd == THIS->invoke_pipe[0]) {
+ n = read(THIS->invoke_pipe[0],&(((unsigned char *)(&(THIS->invoke_pipe_buf)))[THIS->invoke_pipe_buf_ptr]),sizeof(THIS->invoke_pipe_buf) - THIS->invoke_pipe_buf_ptr);
+ if (n > 0) {
+ THIS->invoke_pipe_buf_ptr += (unsigned int)n;
+ if (THIS->invoke_pipe_buf_ptr >= sizeof(THIS->invoke_pipe_buf)) {
+ THIS->invoke_pipe_buf_ptr -= sizeof(THIS->invoke_pipe_buf);
+ ((void (*)(void *))(THIS->invoke_pipe_buf[1]))(THIS->invoke_pipe_buf[0]);
+ }
+ }
+ } else {
+ switch(THIS->sockets[fd_idx].base.type) {
+ case ANODE_SOCKET_DATAGRAM:
+ AnodeSystemTransport__poll_do_read_datagram(THIS,THIS->fds[fd_idx].fd,&THIS->sockets[fd_idx]);
+ break;
+ case ANODE_SOCKET_STREAM_LISTEN:
+ AnodeSystemTransport__poll_do_accept_incoming_connection(THIS,THIS->fds[fd_idx].fd,&THIS->sockets[fd_idx]);
+ break;
+ case ANODE_SOCKET_STREAM_CONNECTION:
+ if (THIS->sockets[fd_idx].base.state == ANODE_SOCKET_CONNECTING)
+ AnodeSystemTransport__poll_do_outgoing_connect(THIS,THIS->fds[fd_idx].fd,&THIS->sockets[fd_idx]);
+ else AnodeSystemTransport__poll_do_read_stream(THIS,THIS->fds[fd_idx].fd,&THIS->sockets[fd_idx]);
+ break;
+ }
+ ++event_count;
+ }
+ }
+
+ if ((THIS->fds[fd_idx].revents & POLLOUT)) {
+ if (THIS->sockets[fd_idx].base.state == ANODE_SOCKET_CONNECTING)
+ AnodeSystemTransport__poll_do_outgoing_connect(THIS,THIS->fds[fd_idx].fd,&THIS->sockets[fd_idx]);
+ else AnodeSystemTransport__poll_do_stream_available_for_write(THIS,THIS->fds[fd_idx].fd,&THIS->sockets[fd_idx]);
+ ++event_count;
+ }
+ }
+ }
+ }
+
+ return event_count;
+}
+
+static int AnodeSystemTransport_supports_address_type(const AnodeTransport *transport,
+ enum AnodeNetworkAddressType at)
+{
+ switch(at) {
+ case ANODE_NETWORK_ADDRESS_IPV4:
+ return 1;
+ case ANODE_NETWORK_ADDRESS_IPV6:
+ return 1;
+ default:
+ if (THIS->base)
+ return THIS->base->supports_address_type(THIS->base,at);
+ return 0;
+ }
+}
+
+static AnodeTransport *AnodeSystemTransport_base_instance(const AnodeTransport *transport)
+{
+ return THIS->base;
+}
+
+static const char *AnodeSystemTransport_class_name(AnodeTransport *transport)
+{
+ return AnodeSystemTransport_CLASS;
+}
+
+static void AnodeSystemTransport_delete(AnodeTransport *transport)
+{
+ close(THIS->invoke_pipe[0]);
+ close(THIS->invoke_pipe[1]);
+
+ AnodeMutex_destroy(&THIS->invoke_pipe_m);
+
+ if (THIS->fds) free(THIS->fds);
+ if (THIS->sockets) free(THIS->sockets);
+
+ if (THIS->base) THIS->base->delete(THIS->base);
+
+ free(transport);
+}
+
+/* ======================================================================== */
+
+AnodeTransport *AnodeSystemTransport_new(AnodeTransport *base)
+{
+ struct AnodeSystemTransport *t;
+ unsigned int entry_idx;
+
+ t = malloc(sizeof(struct AnodeSystemTransport));
+ if (!t) return (AnodeTransport *)0;
+ Anode_zero(t,sizeof(struct AnodeSystemTransport));
+
+ t->interface.invoke = &AnodeSystemTransport_invoke;
+ t->interface.dns_resolve = &AnodeSystemTransport_dns_resolve;
+ t->interface.datagram_listen = &AnodeSystemTransport_datagram_listen;
+ t->interface.stream_listen = &AnodeSystemTransport_stream_listen;
+ t->interface.datagram_send = &AnodeSystemTransport_datagram_send;
+ t->interface.stream_connect = &AnodeSystemTransport_stream_connect;
+ t->interface.stream_start_writing = &AnodeSystemTransport_stream_start_writing;
+ t->interface.stream_stop_writing = &AnodeSystemTransport_stream_stop_writing;
+ t->interface.stream_send = &AnodeSystemTransport_stream_send;
+ t->interface.close = &AnodeSystemTransport_close;
+ t->interface.poll = &AnodeSystemTransport_poll;
+ t->interface.supports_address_type = &AnodeSystemTransport_supports_address_type;
+ t->interface.base_instance = &AnodeSystemTransport_base_instance;
+ t->interface.class_name = &AnodeSystemTransport_class_name;
+ t->interface.delete = &AnodeSystemTransport_delete;
+
+ t->base = base;
+
+ pipe(t->invoke_pipe);
+ fcntl(t->invoke_pipe[0],F_SETFL,O_NONBLOCK);
+ entry_idx = AnodeSystemTransport__add_entry(t);
+ t->fds[entry_idx].fd = t->invoke_pipe[0];
+ t->fds[entry_idx].events = POLLIN;
+ t->fds[entry_idx].revents = 0;
+ AnodeMutex_init(&t->invoke_pipe_m);
+
+ return (AnodeTransport *)t;
+}
diff --git a/attic/historic/anode/libanode/tests/Makefile b/attic/historic/anode/libanode/tests/Makefile
new file mode 100644
index 00000000..a479092c
--- /dev/null
+++ b/attic/historic/anode/libanode/tests/Makefile
@@ -0,0 +1,25 @@
+all: force clean anode-utils-test anode-zone-test aes-test ec-test
+
+aes-test:
+ gcc -Wall -O6 -ftree-vectorize -std=c99 -o aes-test aes-test.c ../aes_digest.c -lcrypto
+
+http_client-test:
+ gcc -O0 -g -std=c99 -o http_client-test http_client-test.c ../anode-utils.c ../misc.c ../http_client.c ../dictionary.c ../iptransport.c ../anode-transport.c -lcrypto
+
+anode-utils-test:
+ gcc -O0 -g -std=c99 -o anode-utils-test anode-utils-test.c ../anode-utils.c ../misc.c
+
+ec-test:
+ gcc -O0 -g -std=c99 -o ec-test ec-test.c ../impl/ec.c ../impl/misc.c -lcrypto
+
+anode-zone-test:
+ gcc -O0 -g -std=c99 -o anode-zone-test anode-zone-test.c ../anode-zone.c ../http_client.c ../dictionary.c ../misc.c ../anode-transport.c ../iptransport.c ../environment.c
+
+system_transport-test:
+ gcc -O0 -g -std=c99 -o system_transport-test system_transport-test.c ../system_transport.c ../network_address.c ../address.c ../aes_digest.c ../impl/misc.c ../impl/thread.c ../impl/dns_txt.c ../impl/aes.c -lresolv -lcrypto
+
+clean: force
+ rm -rf *.dSYM
+ rm -f http_client-test anode-utils-test anode-zone-test ec-test aes-test system_transport-test
+
+force: ;
diff --git a/attic/historic/anode/libanode/tests/aes-test.c b/attic/historic/anode/libanode/tests/aes-test.c
new file mode 100644
index 00000000..bca63b89
--- /dev/null
+++ b/attic/historic/anode/libanode/tests/aes-test.c
@@ -0,0 +1,191 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <time.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "../impl/aes.h"
+#include "../anode.h"
+
+static const unsigned char AES_TEST_KEY[32] = {
+ 0x08,0x09,0x0A,0x0B,0x0D,0x0E,0x0F,0x10,0x12,0x13,0x14,0x15,0x17,0x18,0x19,0x1A,
+ 0x1C,0x1D,0x1E,0x1F,0x21,0x22,0x23,0x24,0x26,0x27,0x28,0x29,0x2B,0x2C,0x2D,0x2E
+};
+static const unsigned char AES_TEST_IN[16] = {
+ 0x06,0x9A,0x00,0x7F,0xC7,0x6A,0x45,0x9F,0x98,0xBA,0xF9,0x17,0xFE,0xDF,0x95,0x21
+};
+static const unsigned char AES_TEST_OUT[16] = {
+ 0x08,0x0e,0x95,0x17,0xeb,0x16,0x77,0x71,0x9a,0xcf,0x72,0x80,0x86,0x04,0x0a,0xe3
+};
+
+static const unsigned char CMAC_TEST_KEY[32] = {
+ 0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe,0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81,
+ 0x1f,0x35,0x2c,0x07,0x3b,0x61,0x08,0xd7,0x2d,0x98,0x10,0xa3,0x09,0x14,0xdf,0xf4
+};
+
+static const unsigned char CMAC_TEST1_OUT[16] = {
+ 0x02,0x89,0x62,0xf6,0x1b,0x7b,0xf8,0x9e,0xfc,0x6b,0x55,0x1f,0x46,0x67,0xd9,0x83
+};
+
+static const unsigned char CMAC_TEST2_IN[16] = {
+ 0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a
+};
+static const unsigned char CMAC_TEST2_OUT[16] = {
+ 0x28,0xa7,0x02,0x3f,0x45,0x2e,0x8f,0x82,0xbd,0x4b,0xf2,0x8d,0x8c,0x37,0xc3,0x5c
+};
+
+static const unsigned char CMAC_TEST3_IN[40] = {
+ 0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a,
+ 0xae,0x2d,0x8a,0x57,0x1e,0x03,0xac,0x9c,0x9e,0xb7,0x6f,0xac,0x45,0xaf,0x8e,0x51,
+ 0x30,0xc8,0x1c,0x46,0xa3,0x5c,0xe4,0x11
+};
+static const unsigned char CMAC_TEST3_OUT[16] = {
+ 0xaa,0xf3,0xd8,0xf1,0xde,0x56,0x40,0xc2,0x32,0xf5,0xb1,0x69,0xb9,0xc9,0x11,0xe6
+};
+
+static const unsigned char CMAC_TEST4_IN[64] = {
+ 0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a,
+ 0xae,0x2d,0x8a,0x57,0x1e,0x03,0xac,0x9c,0x9e,0xb7,0x6f,0xac,0x45,0xaf,0x8e,0x51,
+ 0x30,0xc8,0x1c,0x46,0xa3,0x5c,0xe4,0x11,0xe5,0xfb,0xc1,0x19,0x1a,0x0a,0x52,0xef,
+ 0xf6,0x9f,0x24,0x45,0xdf,0x4f,0x9b,0x17,0xad,0x2b,0x41,0x7b,0xe6,0x6c,0x37,0x10
+};
+static const unsigned char CMAC_TEST4_OUT[16] = {
+ 0xe1,0x99,0x21,0x90,0x54,0x9f,0x6e,0xd5,0x69,0x6a,0x2c,0x05,0x6c,0x31,0x54,0x10
+};
+
+static void test_cmac(const AnodeAesExpandedKey *expkey,const unsigned char *in,unsigned int inlen,const unsigned char *expected)
+{
+ unsigned int i;
+ unsigned char out[16];
+
+ printf("Testing CMAC with %u byte input:\n",inlen);
+ printf(" IN: ");
+ for(i=0;i<inlen;++i)
+ printf("%.2x",(int)in[i]);
+ printf("\n");
+ printf(" EXP: ");
+ for(i=0;i<16;++i)
+ printf("%.2x",(int)expected[i]);
+ printf("\n");
+ Anode_cmac_aes256(expkey,in,inlen,out);
+ printf(" OUT: ");
+ for(i=0;i<16;++i)
+ printf("%.2x",(int)out[i]);
+ printf("\n");
+ if (memcmp(expected,out,16)) {
+ printf("FAILED!\n");
+ exit(1);
+ } else printf("Passed.\n");
+}
+
+static void test_cfb(const AnodeAesExpandedKey *expkey,const unsigned char *in,unsigned int inlen,unsigned char *iv,const unsigned char *expected)
+{
+ unsigned char tmp[131072];
+ unsigned char tmp2[131072];
+ unsigned char tmpiv[16];
+
+ printf("Testing AES-256 CFB mode with %u bytes: ",inlen);
+ fflush(stdout);
+
+ memcpy(tmpiv,iv,16);
+ Anode_aes256_cfb_encrypt(expkey,in,tmp,tmpiv,inlen);
+ if (!memcmp(tmp,expected,inlen)) {
+ printf("FAILED (didn't encrypt)!\n");
+ exit(1);
+ }
+ memcpy(tmpiv,iv,16);
+ Anode_aes256_cfb_decrypt(expkey,tmp,tmp2,tmpiv,inlen);
+ if (memcmp(tmp2,expected,inlen)) {
+ printf("FAILED (didn't encrypt)!\n");
+ exit(1);
+ } else printf("Passed.\n");
+}
+
+static const char *AES_DIGEST_TEST_1 = "test";
+static const char *AES_DIGEST_TEST_2 = "supercalifragilisticexpealidocious";
+static const char *AES_DIGEST_TEST_3 = "12345678";
+static const char *AES_DIGEST_TEST_4 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+
+int main(int argc,char **argv)
+{
+ AnodeAesExpandedKey expkey;
+ unsigned int i;
+ unsigned char aestestbuf[16];
+ unsigned char cfbin[131072];
+ unsigned char iv[16];
+
+ printf("Testing AES-256:");
+ Anode_aes256_expand_key(AES_TEST_KEY,&expkey);
+ printf(" IN: ");
+ for(i=0;i<16;++i)
+ printf("%.2x",(int)AES_TEST_IN[i]);
+ printf("\n");
+ printf(" EXP: ");
+ for(i=0;i<16;++i)
+ printf("%.2x",(int)AES_TEST_OUT[i]);
+ printf("\n");
+ Anode_aes256_encrypt(&expkey,AES_TEST_IN,aestestbuf);
+ printf(" OUT: ");
+ for(i=0;i<16;++i)
+ printf("%.2x",(int)aestestbuf[i]);
+ printf("\n");
+ if (memcmp(AES_TEST_OUT,aestestbuf,16)) {
+ printf("FAILED!\n");
+ return 1;
+ } else printf("Passed.\n");
+ printf("\n");
+
+ Anode_aes256_expand_key(CMAC_TEST_KEY,&expkey);
+ test_cmac(&expkey,(unsigned char *)0,0,CMAC_TEST1_OUT);
+ test_cmac(&expkey,CMAC_TEST2_IN,16,CMAC_TEST2_OUT);
+ test_cmac(&expkey,CMAC_TEST3_IN,40,CMAC_TEST3_OUT);
+ test_cmac(&expkey,CMAC_TEST4_IN,64,CMAC_TEST4_OUT);
+ printf("\n");
+
+ for(i=0;i<131072;++i)
+ cfbin[i] = (unsigned char)(i & 0xff);
+ for(i=0;i<16;++i)
+ iv[i] = (unsigned char)(i & 0xff);
+ for(i=12345;i<131072;i+=7777)
+ test_cfb(&expkey,cfbin,i,iv,cfbin);
+
+ printf("\nTesting AES-DIGEST...\n");
+ printf("0 bytes: ");
+ Anode_aes_digest(cfbin,0,iv);
+ for(i=0;i<16;++i) printf("%.2x",(unsigned int)iv[i]);
+ printf("\n");
+ printf("%d bytes: ",(int)strlen(AES_DIGEST_TEST_1));
+ Anode_aes_digest(AES_DIGEST_TEST_1,strlen(AES_DIGEST_TEST_1),iv);
+ for(i=0;i<16;++i) printf("%.2x",(unsigned int)iv[i]);
+ printf("\n");
+ printf("%d bytes: ",(int)strlen(AES_DIGEST_TEST_2));
+ Anode_aes_digest(AES_DIGEST_TEST_2,strlen(AES_DIGEST_TEST_2),iv);
+ for(i=0;i<16;++i) printf("%.2x",(unsigned int)iv[i]);
+ printf("\n");
+ printf("%d bytes: ",(int)strlen(AES_DIGEST_TEST_3));
+ Anode_aes_digest(AES_DIGEST_TEST_3,strlen(AES_DIGEST_TEST_3),iv);
+ for(i=0;i<16;++i) printf("%.2x",(unsigned int)iv[i]);
+ printf("\n");
+ printf("%d bytes: ",(int)strlen(AES_DIGEST_TEST_4));
+ Anode_aes_digest(AES_DIGEST_TEST_4,strlen(AES_DIGEST_TEST_4),iv);
+ for(i=0;i<16;++i) printf("%.2x",(unsigned int)iv[i]);
+ printf("\n");
+
+ return 0;
+}
+
diff --git a/attic/historic/anode/libanode/tests/anode-secure_random-test.c b/attic/historic/anode/libanode/tests/anode-secure_random-test.c
new file mode 100644
index 00000000..a6983653
--- /dev/null
+++ b/attic/historic/anode/libanode/tests/anode-secure_random-test.c
@@ -0,0 +1,38 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "../anode.h"
+#include "../misc.h"
+
+int main(int argc,char **argv)
+{
+ unsigned char test[10005];
+ unsigned int i;
+ AnodeSecureRandom srng;
+
+ AnodeSecureRandom_init(&srng);
+
+ AnodeSecureRandom_gen_bytes(&srng,test,sizeof(test));
+
+ for(i=0;i<sizeof(test);++i) {
+ printf("%.2x",(unsigned int)test[i]);
+ if ((i % 20) == 19)
+ printf("\n");
+ }
+ printf("\n");
+}
diff --git a/attic/historic/anode/libanode/tests/anode-utils-test.c b/attic/historic/anode/libanode/tests/anode-utils-test.c
new file mode 100644
index 00000000..85bfe324
--- /dev/null
+++ b/attic/historic/anode/libanode/tests/anode-utils-test.c
@@ -0,0 +1,75 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "../anode.h"
+#include "../misc.h"
+
+static const char *testuris[22] = {
+ "http://www.test.com",
+ "http://www.test.com/",
+ "http://www.test.com/path/to/something",
+ "http://user@www.test.com",
+ "http://user@www.test.com/path/to/something",
+ "http://user:password@www.test.com/path/to/something",
+ "http://www.test.com/path/to/something?query=foo&bar=baz",
+ "http://www.test.com/path/to/something#fragment",
+ "http://www.test.com/path/to/something?query=foo&bar=baz#fragment",
+ "http://user:password@www.test.com/path/to/something#fragment",
+ "http://user:password@www.test.com/path/to/something?query=foo&bar=baz#fragment",
+ "http://@www.test.com/",
+ "http://:@www.test.com/",
+ "http://www.test.com:8080/path/to/something",
+ "http://user:password@www.test.com:8080/path/to/something?query=foo#fragment",
+ "http://",
+ "http://www.test.com/path/to/something?#",
+ "http://www.test.com/path/to/something?#fragment",
+ "http:",
+ "http",
+ "mailto:this_is_a_urn@somedomain.com",
+ ""
+};
+
+int main(int argc,char **argv)
+{
+ int i,r;
+ char reconstbuf[2048];
+ char *reconst;
+ AnodeURI uri;
+
+ for(i=0;i<22;++i) {
+ printf("\"%s\":\n",testuris[i]);
+ r = AnodeURI_parse(&uri,testuris[i]);
+ if (r) {
+ printf(" error: %d\n",r);
+ } else {
+ printf(" scheme: %s\n",uri.scheme);
+ printf(" username: %s\n",uri.username);
+ printf(" password: %s\n",uri.password);
+ printf(" host: %s\n",uri.host);
+ printf(" port: %d\n",uri.port);
+ printf(" path: %s\n",uri.path);
+ printf(" query: %s\n",uri.query);
+ printf(" fragment: %s\n",uri.fragment);
+ }
+ reconst = AnodeURI_to_string(&uri,reconstbuf,sizeof(reconstbuf));
+ printf("Reconstituted URI: %s\n",reconst ? reconst : "(null)");
+ printf("\n");
+ }
+
+ return 0;
+}
diff --git a/attic/historic/anode/libanode/tests/anode-zone-test.c b/attic/historic/anode/libanode/tests/anode-zone-test.c
new file mode 100644
index 00000000..08396716
--- /dev/null
+++ b/attic/historic/anode/libanode/tests/anode-zone-test.c
@@ -0,0 +1,47 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "../anode.h"
+#include "../dictionary.h"
+
+static int got_it = 0;
+
+static void zone_lookup_handler(void *ptr,long zone_id,AnodeZone *zone)
+{
+ if (zone)
+ printf("got %.8lx: %d entries\n",(unsigned long)zone_id & 0xffffffff,((struct AnodeDictionary *)zone)->size);
+ else printf("failed.\n");
+ got_it = 1;
+}
+
+int main(int argc,char **argv)
+{
+ AnodeTransportEngine transport;
+
+ Anode_init_ip_transport_engine(&transport);
+
+ AnodeZone_lookup(&transport,0,0,&zone_lookup_handler);
+
+ while (!got_it)
+ transport.poll(&transport);
+
+ transport.destroy(&transport);
+
+ return 0;
+}
diff --git a/attic/historic/anode/libanode/tests/dictionary-test.c b/attic/historic/anode/libanode/tests/dictionary-test.c
new file mode 100644
index 00000000..12a5fb2f
--- /dev/null
+++ b/attic/historic/anode/libanode/tests/dictionary-test.c
@@ -0,0 +1,149 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+#include "../dictionary.h"
+
+static const char *HASH_TESTS[16] = {
+ "test",
+ "testt",
+ "",
+ "foo",
+ "fooo",
+ "1",
+ "2",
+ "3",
+ "4",
+ "11",
+ "22",
+ "33",
+ "44",
+ "adklfjklejrer",
+ "erngnetbekjrq",
+ "erklerqqqqre"
+};
+
+int diterate(void *arg,const char *key,const char *value)
+{
+ printf(" %s: %s\n",key ? key : "(null)",value ? value : "(null)");
+ return 1;
+}
+
+int main(int argc,char **argv)
+{
+ char tmp[1024];
+ char fuzzparam1[16],fuzzparam2[16],fuzzparam3[16];
+ struct AnodeDictionary d;
+ unsigned int i,j,k,cs;
+
+ srandom(time(0));
+
+ printf("Trying out hash function a little...\n");
+ for(i=0;i<16;++i)
+ printf(" %s: %u\n",HASH_TESTS[i],(unsigned int)AnodeDictionary__get_bucket(HASH_TESTS[i]));
+
+ for(cs=0;cs<2;++cs) {
+ printf("\nTesting with case sensitivity = %d\n",cs);
+ AnodeDictionary_init(&d,cs);
+
+ printf("\nTesting dictionary by adding and retrieving some keys...\n");
+ AnodeDictionary_put(&d,"test1","This is the first test");
+ AnodeDictionary_put(&d,"test2","This is the second test");
+ AnodeDictionary_put(&d,"test3","This is the third test (lower case)");
+ AnodeDictionary_put(&d,"TEST3","This is the third test (UPPER CASE)");
+ AnodeDictionary_iterate(&d,(void *)0,&diterate);
+ if (d.size != (cs ? 4 : 3)) {
+ printf("Failed (size).\n");
+ return 1;
+ }
+
+ AnodeDictionary_clear(&d);
+ if (d.size||(AnodeDictionary_get(&d,"test1"))) {
+ printf("Failed (clear).\n");
+ return 1;
+ }
+
+ printf("\nTesting read, trial 1: simple key=value with unterminated line\n");
+ strcpy(tmp,"foo=bar\nbar=baz\ntest1=Happy happy joyjoy!\ntest2=foobarbaz\nlinewithnocr=thisworked");
+ AnodeDictionary_read(&d,tmp,"\r\n","=","",'\\',0,0);
+ printf("Results:\n");
+ AnodeDictionary_iterate(&d,(void *)0,&diterate);
+ AnodeDictionary_clear(&d);
+
+ printf("\nTesting read, trial 2: key=value with escape chars, escaped CRs\n");
+ strcpy(tmp,"foo=bar\r\nbar==baz\nte\\=st1=\\=Happy happy joyjoy!\ntest2=foobarbaz\\\nfoobarbaz on next line\r\n");
+ AnodeDictionary_read(&d,tmp,"\r\n","=","",'\\',0,0);
+ printf("Results:\n");
+ AnodeDictionary_iterate(&d,(void *)0,&diterate);
+ AnodeDictionary_clear(&d);
+
+ printf("\nTesting read, trial 3: HTTP header-like dictionary\n");
+ strcpy(tmp,"Host: some.host.net\r\nX-Some-Header: foo bar\r\nX-Some-Other-Header: y0y0y0y0y0\r\n");
+ AnodeDictionary_read(&d,tmp,"\r\n",": ","",0,0,0);
+ printf("Results:\n");
+ AnodeDictionary_iterate(&d,(void *)0,&diterate);
+ AnodeDictionary_clear(&d);
+
+ printf("\nTesting read, trial 4: single line key/value\n");
+ strcpy(tmp,"Header: one line only");
+ AnodeDictionary_read(&d,tmp,"\r\n",": ","",0,0,0);
+ printf("Results:\n");
+ AnodeDictionary_iterate(&d,(void *)0,&diterate);
+ AnodeDictionary_clear(&d);
+
+ printf("\nFuzzing dictionary reader...\n"); fflush(stdout);
+ for(i=0;i<200000;++i) {
+ j = random() % (sizeof(tmp) - 1);
+ for(k=0;k<j;++k) {
+ tmp[k] = (char)((unsigned int)random() >> 3);
+ if (!tmp[k]) tmp[k] = 1;
+ }
+ tmp[j] = (char)0;
+
+ j = random() % (sizeof(fuzzparam1) - 1);
+ for(k=0;k<j;++k) {
+ fuzzparam1[k] = (char)((unsigned int)random() >> 3);
+ if (!fuzzparam1[k]) fuzzparam1[k] = 1;
+ }
+ fuzzparam1[j] = (char)0;
+
+ j = random() % (sizeof(fuzzparam2) - 1);
+ for(k=0;k<j;++k) {
+ fuzzparam1[k] = (char)((unsigned int)random() >> 3);
+ if (!fuzzparam2[k]) fuzzparam2[k] = 1;
+ }
+ fuzzparam2[j] = (char)0;
+
+ j = random() % (sizeof(fuzzparam3) - 1);
+ for(k=0;k<j;++k) {
+ fuzzparam3[k] = (char)((unsigned int)random() >> 3);
+ if (!fuzzparam3[k]) fuzzparam3[k] = 1;
+ }
+ fuzzparam3[j] = (char)0;
+
+ AnodeDictionary_read(&d,tmp,fuzzparam1,fuzzparam2,fuzzparam3,random() & 3,random() & 1,random() & 1);
+ AnodeDictionary_clear(&d);
+ }
+
+ AnodeDictionary_destroy(&d);
+ }
+
+ return 0;
+}
diff --git a/attic/historic/anode/libanode/tests/ec-test.c b/attic/historic/anode/libanode/tests/ec-test.c
new file mode 100644
index 00000000..49f04265
--- /dev/null
+++ b/attic/historic/anode/libanode/tests/ec-test.c
@@ -0,0 +1,97 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "../impl/ec.h"
+#include "../impl/misc.h"
+
+#define TEST_KEY_LEN 128
+#define AnodeEC_key_to_hex(k,b,l) Anode_to_hex((k)->key,(k)->bytes,(b),l)
+
+int main(int argc,char **argv)
+{
+ struct AnodeECKeyPair pair1;
+ struct AnodeECKeyPair pair2;
+ struct AnodeECKeyPair pair3;
+ unsigned char key[TEST_KEY_LEN];
+ char str[16384];
+
+ printf("Creating key pair #1...\n");
+ if (!AnodeECKeyPair_generate(&pair1)) {
+ printf("Could not create key pair.\n");
+ return 1;
+ }
+ AnodeEC_key_to_hex(&pair1.pub,str,sizeof(str));
+ printf("Public: %s\n",str);
+ AnodeEC_key_to_hex(&pair1.priv,str,sizeof(str));
+ printf("Private: %s\n\n",str);
+
+ printf("Creating key pair #2...\n");
+ if (!AnodeECKeyPair_generate(&pair2)) {
+ printf("Could not create key pair.\n");
+ return 1;
+ }
+ AnodeEC_key_to_hex(&pair2.pub,str,sizeof(str));
+ printf("Public: %s\n",str);
+ AnodeEC_key_to_hex(&pair2.priv,str,sizeof(str));
+ printf("Private: %s\n\n",str);
+
+ printf("Key agreement between public #2 and private #1...\n");
+ if (!AnodeECKeyPair_agree(&pair1,&pair2.pub,key,TEST_KEY_LEN)) {
+ printf("Agreement failed.\n");
+ return 1;
+ }
+ Anode_to_hex(key,TEST_KEY_LEN,str,sizeof(str));
+ printf("Agreed secret: %s\n\n",str);
+
+ printf("Key agreement between public #1 and private #2...\n");
+ if (!AnodeECKeyPair_agree(&pair2,&pair1.pub,key,TEST_KEY_LEN)) {
+ printf("Agreement failed.\n");
+ return 1;
+ }
+ Anode_to_hex(key,TEST_KEY_LEN,str,sizeof(str));
+ printf("Agreed secret: %s\n\n",str);
+
+ printf("Testing key pair init function (init #3 from #2's parts)...\n");
+ if (!AnodeECKeyPair_init(&pair3,&(pair2.pub),&(pair2.priv))) {
+ printf("Init failed.\n");
+ return 1;
+ }
+
+ printf("Key agreement between public #1 and private #3...\n");
+ if (!AnodeECKeyPair_agree(&pair3,&pair1.pub,key,TEST_KEY_LEN)) {
+ printf("Agreement failed.\n");
+ return 1;
+ }
+ Anode_to_hex(key,TEST_KEY_LEN,str,sizeof(str));
+ printf("Agreed secret: %s\n\n",str);
+
+ printf("Key agreement between public #1 and private #1...\n");
+ if (!AnodeECKeyPair_agree(&pair1,&pair1.pub,key,TEST_KEY_LEN)) {
+ printf("Agreement failed.\n");
+ return 1;
+ }
+ Anode_to_hex(key,TEST_KEY_LEN,str,sizeof(str));
+ printf("Agreed secret (should not match): %s\n\n",str);
+
+ AnodeECKeyPair_destroy(&pair1);
+ AnodeECKeyPair_destroy(&pair2);
+ AnodeECKeyPair_destroy(&pair3);
+
+ return 0;
+}
diff --git a/attic/historic/anode/libanode/tests/environment-test.c b/attic/historic/anode/libanode/tests/environment-test.c
new file mode 100644
index 00000000..c481a129
--- /dev/null
+++ b/attic/historic/anode/libanode/tests/environment-test.c
@@ -0,0 +1,28 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "../environment.h"
+
+int main(int argc,char **argv)
+{
+ const char *cache = Anode_get_cache();
+
+ printf("Cache folder: %s\n",cache ? cache : "(null)");
+
+ return 0;
+}
diff --git a/attic/historic/anode/libanode/tests/http_client-test.c b/attic/historic/anode/libanode/tests/http_client-test.c
new file mode 100644
index 00000000..e1f93967
--- /dev/null
+++ b/attic/historic/anode/libanode/tests/http_client-test.c
@@ -0,0 +1,233 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <openssl/sha.h>
+#include "../anode.h"
+#include "../misc.h"
+#include "../http_client.h"
+#include "../dictionary.h"
+
+struct TestCase
+{
+ int method;
+ AnodeURI uri;
+ const void *client_data;
+ unsigned int client_data_len;
+ const char *expected_sha1;
+ char actual_sha1[64];
+ int got_it;
+ int keepalive;
+ struct TestCase *next;
+};
+
+#define NUM_TEST_CASES 7
+static struct TestCase test_cases[NUM_TEST_CASES];
+
+static void init_test_cases(int keepalive)
+{
+ AnodeURI_parse(&(test_cases[0].uri),"http://zerotier.com/for_unit_tests/test1.txt");
+ test_cases[0].method = ANODE_HTTP_GET;
+ test_cases[0].client_data_len = 0;
+ test_cases[0].expected_sha1 = "0828324174b10cc867b7255a84a8155cf89e1b8b";
+ test_cases[0].actual_sha1[0] = (char)0;
+ test_cases[0].got_it = 0;
+ test_cases[0].keepalive = keepalive;
+ test_cases[0].next = &(test_cases[1]);
+
+ AnodeURI_parse(&(test_cases[1].uri),"http://zerotier.com/for_unit_tests/test2.bin");
+ test_cases[1].method = ANODE_HTTP_GET;
+ test_cases[1].client_data_len = 0;
+ test_cases[1].expected_sha1 = "6b67c635786ab52666211d02412c0d0f0372980d";
+ test_cases[1].actual_sha1[0] = (char)0;
+ test_cases[1].got_it = 0;
+ test_cases[1].keepalive = keepalive;
+ test_cases[1].next = &(test_cases[2]);
+
+ AnodeURI_parse(&(test_cases[2].uri),"http://zerotier.com/for_unit_tests/test3.bin");
+ test_cases[2].method = ANODE_HTTP_GET;
+ test_cases[2].client_data_len = 0;
+ test_cases[2].expected_sha1 = "efa7722029fdbb6abd0e3ed32a0b44bfb982cff0";
+ test_cases[2].actual_sha1[0] = (char)0;
+ test_cases[2].got_it = 0;
+ test_cases[2].keepalive = keepalive;
+ test_cases[2].next = &(test_cases[3]);
+
+ AnodeURI_parse(&(test_cases[3].uri),"http://zerotier.com/for_unit_tests/test4.bin");
+ test_cases[3].method = ANODE_HTTP_GET;
+ test_cases[3].client_data_len = 0;
+ test_cases[3].expected_sha1 = "da39a3ee5e6b4b0d3255bfef95601890afd80709";
+ test_cases[3].actual_sha1[0] = (char)0;
+ test_cases[3].got_it = 0;
+ test_cases[3].keepalive = keepalive;
+ test_cases[3].next = &(test_cases[4]);
+
+ AnodeURI_parse(&(test_cases[4].uri),"http://zerotier.com/for_unit_tests/echo.php?echo=foobar");
+ test_cases[4].method = ANODE_HTTP_GET;
+ test_cases[4].client_data_len = 0;
+ test_cases[4].expected_sha1 = "8843d7f92416211de9ebb963ff4ce28125932878";
+ test_cases[4].actual_sha1[0] = (char)0;
+ test_cases[4].got_it = 0;
+ test_cases[4].keepalive = keepalive;
+ test_cases[4].next = &(test_cases[5]);
+
+ AnodeURI_parse(&(test_cases[5].uri),"http://zerotier.com/for_unit_tests/echo.php");
+ test_cases[5].method = ANODE_HTTP_POST;
+ test_cases[5].client_data = "echo=foobar";
+ test_cases[5].client_data_len = strlen((char *)test_cases[5].client_data);
+ test_cases[5].expected_sha1 = "8843d7f92416211de9ebb963ff4ce28125932878";
+ test_cases[5].actual_sha1[0] = (char)0;
+ test_cases[5].got_it = 0;
+ test_cases[5].keepalive = keepalive;
+ test_cases[5].next = &(test_cases[6]);
+
+ AnodeURI_parse(&(test_cases[6].uri),"http://zerotier.com/for_unit_tests/test3.bin");
+ test_cases[6].method = ANODE_HTTP_HEAD;
+ test_cases[6].client_data_len = 0;
+ test_cases[6].expected_sha1 = "da39a3ee5e6b4b0d3255bfef95601890afd80709";
+ test_cases[6].actual_sha1[0] = (char)0;
+ test_cases[6].got_it = 0;
+ test_cases[6].keepalive = keepalive;
+ test_cases[6].next = 0;
+}
+
+static int http_handler_dump_headers(void *arg,const char *key,const char *value)
+{
+ printf(" H %s: %s\n",key,value);
+ return 1;
+}
+
+static void http_handler(struct AnodeHttpClient *client)
+{
+ const char *method = "???";
+ char buf[1024];
+ unsigned char sha[20];
+ struct TestCase *test = (struct TestCase *)client->ptr[0];
+
+ switch(client->method) {
+ case ANODE_HTTP_GET:
+ method = "GET";
+ break;
+ case ANODE_HTTP_HEAD:
+ method = "HEAD";
+ break;
+ case ANODE_HTTP_POST:
+ method = "POST";
+ break;
+ }
+
+ if (client->response.code == 200) {
+ SHA1((unsigned char *)client->response.data,client->response.data_length,sha);
+ Anode_to_hex(sha,20,test->actual_sha1,sizeof(test->actual_sha1));
+ printf("%s %s\n * SHA1: %s exp: %s\n",method,AnodeURI_to_string(&(test->uri),buf,sizeof(buf)),test->actual_sha1,test->expected_sha1);
+ if (strcmp(test->actual_sha1,test->expected_sha1))
+ printf(" ! SHA1 MISMATCH!\n");
+ AnodeDictionary_iterate(&(client->response.headers),0,&http_handler_dump_headers);
+ } else printf("%s %s: ERROR: %d\n",method,AnodeURI_to_string(&(test->uri),buf,sizeof(buf)),client->response.code);
+
+ test->got_it = 1;
+
+ if (!test->keepalive)
+ AnodeHttpClient_free(client);
+ else {
+ test = test->next;
+ if (test) {
+ memcpy((void *)&(client->uri),(const void *)&(test->uri),sizeof(AnodeURI));
+
+ client->data = test->client_data;
+ client->data_length = test->client_data_len;
+ client->ptr[0] = test;
+ client->keepalive = test->keepalive;
+ client->method = test->method;
+ client->handler = &http_handler;
+
+ AnodeHttpClient_send(client);
+ } else {
+ AnodeHttpClient_free(client);
+ }
+ }
+}
+
+int main(int argc,char **argv)
+{
+ struct AnodeHttpClient *client;
+ AnodeTransportEngine transport_engine;
+ int i;
+
+ if (Anode_init_ip_transport_engine(&transport_engine)) {
+ printf("Failed (transport engine init)\n");
+ return 1;
+ }
+
+ printf("Testing without keepalive...\n\n");
+ init_test_cases(0);
+ for(i=0;i<NUM_TEST_CASES;++i) {
+ client = AnodeHttpClient_new(&transport_engine);
+
+ memcpy((void *)&(client->uri),(const void *)&(test_cases[i].uri),sizeof(AnodeURI));
+ client->data = test_cases[i].client_data;
+ client->data_length = test_cases[i].client_data_len;
+ client->ptr[0] = &test_cases[i];
+ client->keepalive = test_cases[i].keepalive;
+ client->method = test_cases[i].method;
+ client->handler = &http_handler;
+
+ AnodeHttpClient_send(client);
+ }
+
+ for(;;) {
+ for(i=0;i<NUM_TEST_CASES;++i) {
+ if (!test_cases[i].got_it)
+ break;
+ }
+ if (i == NUM_TEST_CASES)
+ break;
+ transport_engine.poll(&transport_engine);
+ }
+ printf("\n\n");
+
+ printf("Testing with keepalive...\n\n");
+ init_test_cases(1);
+
+ client = AnodeHttpClient_new(&transport_engine);
+
+ i = 0;
+ memcpy((void *)&(client->uri),(const void *)&(test_cases[i].uri),sizeof(AnodeURI));
+ client->data = test_cases[i].client_data;
+ client->data_length = test_cases[i].client_data_len;
+ client->ptr[0] = &test_cases[i];
+ client->keepalive = test_cases[i].keepalive;
+ client->method = test_cases[i].method;
+ client->handler = &http_handler;
+
+ AnodeHttpClient_send(client);
+
+ for(;;) {
+ for(i=0;i<NUM_TEST_CASES;++i) {
+ if (!test_cases[i].got_it)
+ break;
+ }
+ if (i == NUM_TEST_CASES)
+ break;
+ transport_engine.poll(&transport_engine);
+ }
+
+ transport_engine.destroy(&transport_engine);
+
+ return 0;
+}
diff --git a/attic/historic/anode/libanode/tests/misc-test.c b/attic/historic/anode/libanode/tests/misc-test.c
new file mode 100644
index 00000000..e5b9085f
--- /dev/null
+++ b/attic/historic/anode/libanode/tests/misc-test.c
@@ -0,0 +1,137 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+#include "../misc.h"
+
+int main(int argc,char **argv)
+{
+ const char *base32TestStr = "asdf";
+ char *fields[16];
+ char buf[1024];
+ char buf2[1024];
+ char buf3[4096];
+ unsigned int i;
+ unsigned long tmpl,tmpl2;
+ unsigned long long tmp64;
+
+ srand(time(0));
+
+ Anode_base32_5_to_8((const unsigned char *)base32TestStr,buf);
+ printf("Base32 from test string: %s\n",buf);
+ Anode_base32_8_to_5("MFZWIZQA",(unsigned char *)buf2);
+ printf("Test string from Base32 (upper case): %s\n",buf2);
+ Anode_base32_8_to_5("mfzwizqa",(unsigned char *)buf2);
+ printf("Test string from Base32 (lower case): %s\n",buf2);
+ printf("Testing variable length encoding/decoded with pad5 functions...\n");
+ for(i=0;i<1024;++i) {
+ tmpl = rand() % (sizeof(buf) - 8);
+ if (!tmpl)
+ tmpl = 1;
+ for(tmpl2=0;tmpl2<tmpl;++tmpl2)
+ buf[tmpl2] = (buf2[tmpl2] = (char)(rand() >> 3));
+ if (!Anode_base32_encode_pad5(buf2,tmpl,buf3,sizeof(buf3))) {
+ printf("Failed (encode failed).\n");
+ return 1;
+ }
+ memset(buf2,0,sizeof(buf2));
+ if (!Anode_base32_decode_pad5(buf3,buf2,sizeof(buf2))) {
+ printf("Failed (decode failed).\n");
+ return 1;
+ }
+ if (memcmp(buf,buf2,tmpl)) {
+ printf("Failed (compare failed).\n");
+ return 1;
+ }
+ }
+
+ printf("Anode_htonll(0x0102030405060708) == 0x%.16llx\n",tmp64 = Anode_htonll(0x0102030405060708ULL));
+ printf("Anode_ntohll(0x%.16llx) == 0x%.16llx\n",tmp64,Anode_ntohll(tmp64));
+ if (Anode_ntohll(tmp64) != 0x0102030405060708ULL) {
+ printf("Failed.\n");
+ return 1;
+ }
+
+ strcpy(buf,"foo bar baz");
+ Anode_trim(buf);
+ printf("Testing string trim: 'foo bar baz' -> '%s'\n",buf);
+ strcpy(buf,"foo bar baz ");
+ Anode_trim(buf);
+ printf("Testing string trim: 'foo bar baz ' -> '%s'\n",buf);
+ strcpy(buf," foo bar baz");
+ Anode_trim(buf);
+ printf("Testing string trim: ' foo bar baz' -> '%s'\n",buf);
+ strcpy(buf," foo bar baz ");
+ Anode_trim(buf);
+ printf("Testing string trim: ' foo bar baz ' -> '%s'\n",buf);
+ strcpy(buf,"");
+ Anode_trim(buf);
+ printf("Testing string trim: '' -> '%s'\n",buf);
+ strcpy(buf," ");
+ Anode_trim(buf);
+ printf("Testing string trim: ' ' -> '%s'\n",buf);
+
+ printf("Testing string split.\n");
+ strcpy(buf,"66.246.138.121,5323,0");
+ i = Anode_split(buf,';',fields,16);
+ if (i != 1) {
+ printf("Failed.\n");
+ return 1;
+ } else printf("Fields: %s\n",fields[0]);
+ strcpy(buf,"a;b;c");
+ i = Anode_split(buf,';',fields,16);
+ if (i != 3) {
+ printf("Failed.\n");
+ return 1;
+ } else printf("Fields: %s %s %s\n",fields[0],fields[1],fields[2]);
+ strcpy(buf,";;");
+ i = Anode_split(buf,';',fields,16);
+ if (i != 3) {
+ printf("Failed.\n");
+ return 1;
+ } else printf("Fields: %s %s %s\n",fields[0],fields[1],fields[2]);
+ strcpy(buf,"a;b;");
+ i = Anode_split(buf,';',fields,16);
+ if (i != 3) {
+ printf("Failed.\n");
+ return 1;
+ } else printf("Fields: %s %s %s\n",fields[0],fields[1],fields[2]);
+ strcpy(buf,"a;;c");
+ i = Anode_split(buf,';',fields,16);
+ if (i != 3) {
+ printf("Failed.\n");
+ return 1;
+ } else printf("Fields: %s %s %s\n",fields[0],fields[1],fields[2]);
+ strcpy(buf,";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;");
+ i = Anode_split(buf,';',fields,16);
+ if (i != 16) {
+ printf("Failed.\n");
+ return 1;
+ }
+ strcpy(buf,"");
+ i = Anode_split(buf,';',fields,16);
+ if (i != 0) {
+ printf("Failed.\n");
+ return 1;
+ }
+ printf("Passed.\n");
+
+ return 0;
+}
diff --git a/attic/historic/anode/libanode/tests/system_transport-test.c b/attic/historic/anode/libanode/tests/system_transport-test.c
new file mode 100644
index 00000000..bda575ed
--- /dev/null
+++ b/attic/historic/anode/libanode/tests/system_transport-test.c
@@ -0,0 +1,70 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include "../anode.h"
+#include "../impl/thread.h"
+
+static int do_client()
+{
+ AnodeTransport *st;
+ AnodeSocket *udp_sock;
+ int run = 1;
+
+ st = AnodeSystemTransport_new(NULL);
+ if (!st) {
+ printf("FAILED: unable to construct AnodeSystemTransport.\n");
+ return -1;
+ }
+ printf("Created AnodeSystemTransport.\n");
+
+ while (run)
+ st->poll(st);
+}
+
+static int do_server()
+{
+ AnodeTransport *st;
+ AnodeSocket *udp_sock;
+ AnodeSocket *tcp_sock;
+ int run = 1;
+
+ st = AnodeSystemTransport_new(NULL);
+ if (!st) {
+ printf("FAILED: unable to construct AnodeSystemTransport.\n");
+ return -1;
+ }
+ printf("Created AnodeSystemTransport.\n");
+
+ while (run)
+ st->poll(st);
+}
+
+int main(int argc,char **argv)
+{
+ if (argc == 2) {
+ if (!strcmp(argv[1],"client"))
+ return do_client();
+ else if (!strcmp(argv[1],"server"))
+ return do_server();
+ }
+
+ printf("Usage: system_transport-test <client / server>\n");
+ return -1;
+}
diff --git a/attic/historic/anode/libanode/uri.c b/attic/historic/anode/libanode/uri.c
new file mode 100644
index 00000000..ca644b6a
--- /dev/null
+++ b/attic/historic/anode/libanode/uri.c
@@ -0,0 +1,185 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "impl/misc.h"
+#include "anode.h"
+
+int AnodeURI_parse(AnodeURI *parsed_uri,const char *uri_string)
+{
+ char buf[sizeof(AnodeURI)];
+ unsigned long ptr = 0;
+ char c;
+ char *p1,*p2;
+
+ Anode_zero((void *)parsed_uri,sizeof(AnodeURI));
+
+ /* Get the scheme */
+ for(;;) {
+ c = *(uri_string++);
+ if (!c) {
+ parsed_uri->scheme[ptr] = (char)0;
+ return ANODE_ERR_INVALID_URI;
+ } else if (c == ':') {
+ parsed_uri->scheme[ptr] = (char)0;
+ break;
+ } else {
+ parsed_uri->scheme[ptr++] = c;
+ if (ptr == sizeof(parsed_uri->scheme))
+ return ANODE_ERR_BUFFER_TOO_SMALL;
+ }
+ }
+
+ if (*uri_string == '/') {
+ /* If it starts with /, it's a URL */
+
+ /* Skip double slash */
+ if (!(*(++uri_string)))
+ return 0; /* Scheme with no path */
+ if (*uri_string == '/') {
+ if (!(*(++uri_string)))
+ return 0; /* Scheme with no path */
+ }
+
+ /* Get the host section and put it in buf[] */
+ ptr = 0;
+ while ((*uri_string)&&(*uri_string != '/')) {
+ buf[ptr++] = *(uri_string++);
+ if (ptr == sizeof(buf))
+ return ANODE_ERR_BUFFER_TOO_SMALL;
+ }
+ buf[ptr] = (char)0;
+
+ /* Parse host section for host, username, password, and port */
+ if (buf[0]) {
+ p1 = (char *)Anode_strchr(buf,'@');
+ if (p1) {
+ *(p1++) = (char)0;
+ if (*p1) {
+ p2 = (char *)Anode_strchr(buf,':');
+ if (p2) {
+ *(p2++) = (char)0;
+ Anode_str_copy(parsed_uri->password,p2,sizeof(parsed_uri->password));
+ }
+ Anode_str_copy(parsed_uri->username,buf,sizeof(parsed_uri->username));
+ } else return ANODE_ERR_INVALID_URI;
+ } else p1 = buf;
+
+ p2 = (char *)Anode_strchr(p1,':');
+ if (p2) {
+ *(p2++) = (char)0;
+ if (*p2)
+ parsed_uri->port = (int)strtoul(p2,(char **)0,10);
+ }
+ Anode_str_copy(parsed_uri->host,p1,sizeof(parsed_uri->host));
+ }
+
+ /* Get the path, query, and fragment section and put it in buf[] */
+ ptr = 0;
+ while ((buf[ptr++] = *(uri_string++))) {
+ if (ptr == sizeof(buf))
+ return ANODE_ERR_BUFFER_TOO_SMALL;
+ }
+
+ /* Parse path section for path, query, and fragment */
+ if (buf[0]) {
+ p1 = (char *)Anode_strchr(buf,'?');
+ if (p1) {
+ *(p1++) = (char)0;
+ p2 = (char *)Anode_strchr(p1,'#');
+ if (p2) {
+ *(p2++) = (char)0;
+ Anode_str_copy(parsed_uri->fragment,p2,sizeof(parsed_uri->fragment));
+ }
+ Anode_str_copy(parsed_uri->query,p1,sizeof(parsed_uri->query));
+ } else {
+ p2 = (char *)Anode_strchr(buf,'#');
+ if (p2) {
+ *(p2++) = (char)0;
+ Anode_str_copy(parsed_uri->fragment,p2,sizeof(parsed_uri->fragment));
+ }
+ }
+ Anode_str_copy(parsed_uri->path,buf,sizeof(parsed_uri->path));
+ }
+ } else {
+ /* Otherwise, it's a URN and what remains is all path */
+ ptr = 0;
+ while ((parsed_uri->path[ptr++] = *(uri_string++))) {
+ if (ptr == sizeof(parsed_uri->path))
+ return ANODE_ERR_BUFFER_TOO_SMALL;
+ }
+ }
+
+ return 0;
+}
+
+char *AnodeURI_to_string(const AnodeURI *uri,char *buf,int len)
+{
+ int i = 0;
+ char portbuf[16];
+ const char *p;
+
+ p = uri->scheme;
+ while (*p) { buf[i++] = *(p++); if (i >= len) return (char *)0; }
+
+ buf[i++] = ':'; if (i >= len) return (char *)0;
+
+ if (uri->host[0]) {
+ buf[i++] = '/'; if (i >= len) return (char *)0;
+ buf[i++] = '/'; if (i >= len) return (char *)0;
+
+ if (uri->username[0]) {
+ p = uri->username;
+ while (*p) { buf[i++] = *(p++); if (i >= len) return (char *)0; }
+ if (uri->password[0]) {
+ buf[i++] = ':'; if (i >= len) return (char *)0;
+ p = uri->password;
+ while (*p) { buf[i++] = *(p++); if (i >= len) return (char *)0; }
+ }
+ buf[i++] = '@'; if (i >= len) return (char *)0;
+ }
+
+ p = uri->host;
+ while (*p) { buf[i++] = *(p++); if (i >= len) return (char *)0; }
+
+ if ((uri->port > 0)&&(uri->port <= 0xffff)) {
+ buf[i++] = ':'; if (i >= len) return (char *)0;
+ snprintf(portbuf,sizeof(portbuf),"%d",uri->port);
+ p = portbuf;
+ while (*p) { buf[i++] = *(p++); if (i >= len) return (char *)0; }
+ }
+ }
+
+ p = uri->path;
+ while (*p) { buf[i++] = *(p++); if (i >= len) return (char *)0; }
+
+ if (uri->query[0]) {
+ buf[i++] = '?'; if (i >= len) return (char *)0;
+ p = uri->query;
+ while (*p) { buf[i++] = *(p++); if (i >= len) return (char *)0; }
+ }
+
+ if (uri->fragment[0]) {
+ buf[i++] = '#'; if (i >= len) return (char *)0;
+ p = uri->fragment;
+ while (*p) { buf[i++] = *(p++); if (i >= len) return (char *)0; }
+ }
+
+ buf[i] = (char)0;
+
+ return buf;
+}
diff --git a/attic/historic/anode/libanode/utils/anode-make-identity.c b/attic/historic/anode/libanode/utils/anode-make-identity.c
new file mode 100644
index 00000000..99a3897a
--- /dev/null
+++ b/attic/historic/anode/libanode/utils/anode-make-identity.c
@@ -0,0 +1,50 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include "../anode.h"
+#include "../impl/misc.h"
+#include "../impl/types.h"
+
+int main(int argc,char **argv)
+{
+ char str[1024];
+ AnodeZone zone;
+ AnodeIdentity identity;
+
+ if (argc < 2) {
+ printf("Usage: anode-make-identity <32-bit zone ID hex>\n");
+ return 0;
+ }
+
+ *((uint32_t *)zone.bits) = htonl((uint32_t)strtoul(argv[1],(char **)0,16));
+
+ if (AnodeIdentity_generate(&identity,&zone,ANODE_ADDRESS_ANODE_256_40)) {
+ fprintf(stderr,"Error: identity key pair generation failed (check build settings).\n");
+ return 1;
+ }
+ if (AnodeIdentity_to_string(&identity,str,sizeof(str)) <= 0) {
+ fprintf(stderr,"Error: internal error converting identity to string.\n");
+ return -1;
+ }
+
+ printf("%s\n",str);
+
+ return 0;
+}
diff --git a/attic/historic/anode/libanode/zone.c b/attic/historic/anode/libanode/zone.c
new file mode 100644
index 00000000..a6e397ae
--- /dev/null
+++ b/attic/historic/anode/libanode/zone.c
@@ -0,0 +1,184 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "impl/types.h"
+#include "impl/misc.h"
+#include "impl/dictionary.h"
+#include "impl/environment.h"
+#include "impl/http_client.h"
+#include "anode.h"
+
+static const char *_MONTHS[12] = { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" };
+static const char *_DAYS_OF_WEEK[7] = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" };
+static inline unsigned long get_file_time_for_http(const char *path,char *buf,unsigned int len)
+{
+ struct stat st;
+ struct tm *gmt;
+
+ if (!stat(path,(struct stat *)&st)) {
+ gmt = gmtime(&st.st_mtime);
+ if (gmt) {
+ snprintf(buf,len,"%s, %d %s %d %d:%d:%d GMT",
+ _DAYS_OF_WEEK[gmt->tm_wday],
+ gmt->tm_mday,
+ _MONTHS[gmt->tm_mon],
+ (1900 + gmt->tm_year),
+ gmt->tm_hour,
+ gmt->tm_min,
+ gmt->tm_sec);
+ buf[len - 1] = (char)0;
+ return (unsigned long)st.st_size;
+ }
+ }
+
+ return 0;
+}
+
+struct AnodeZoneLookupJob
+{
+ char cached_zone_file[2048];
+ struct AnodeDictionary *zone_dict;
+ AnodeZone zone;
+ void *ptr;
+ void (*zone_lookup_handler)(void *,const AnodeZone *,AnodeZoneFile *);
+ int had_cached_zone;
+};
+
+static void AnodeZone_lookup_http_handler(struct AnodeHttpClient *client)
+{
+ char *data_tmp;
+ struct AnodeZoneLookupJob *job = (struct AnodeZoneLookupJob *)client->ptr[0];
+ FILE *zf;
+
+ if ((client->response.code == 200)&&(client->response.data_length > 0)) {
+ zf = fopen(job->cached_zone_file,"w");
+ if (zf) {
+ fwrite(client->response.data,1,client->response.data_length,zf);
+ fclose(zf);
+ }
+
+ data_tmp = (char *)malloc(client->response.data_length + 1);
+ Anode_memcpy((void *)data_tmp,client->response.data,client->response.data_length);
+ data_tmp[client->response.data_length] = (char)0;
+
+ AnodeDictionary_clear(job->zone_dict);
+ AnodeDictionary_read(
+ job->zone_dict,
+ data_tmp,
+ "\r\n",
+ "=",
+ ";",
+ '\\',
+ 1,1);
+
+ free((void *)data_tmp);
+
+ job->zone_lookup_handler(job->ptr,&job->zone,(AnodeZoneFile *)job->zone_dict);
+ } else if (job->had_cached_zone)
+ job->zone_lookup_handler(job->ptr,&job->zone,(AnodeZoneFile *)job->zone_dict);
+ else {
+ AnodeDictionary_destroy(job->zone_dict);
+ free((void *)job->zone_dict);
+ job->zone_lookup_handler(job->ptr,&job->zone,(AnodeZoneFile *)0);
+ }
+
+ free((void *)job);
+ AnodeHttpClient_free(client);
+}
+
+void AnodeZone_lookup(
+ AnodeTransportEngine *transport,
+ const AnodeZone *zone,
+ void *ptr,
+ void (*zone_lookup_handler)(void *,const AnodeZone *,AnodeZone *))
+{
+ char cached_zones_folder[2048];
+ char cached_zone_file[2048];
+ char if_modified_since[256];
+ unsigned long file_size;
+ struct AnodeZoneLookupJob *job;
+ struct AnodeHttpClient *client;
+ char *file_data;
+ FILE *zf;
+
+ if (Anode_get_cache_sub("zones",cached_zones_folder,sizeof(cached_zones_folder))) {
+ snprintf(cached_zone_file,sizeof(cached_zone_file),"%s%c%.2x%.2x%.2x%.2x.z",cached_zones_folder,ANODE_PATH_SEPARATOR,(unsigned int)zone->bits[0],(unsigned int)zone->bits[1],(unsigned int)zone->bits[2],(unsigned int)zone->bits[3]);
+ cached_zone_file[sizeof(cached_zone_file)-1] = (char)0;
+
+ job = (struct AnodeZoneLookupJob *)malloc(sizeof(struct AnodeZoneLookupJob));
+ Anode_str_copy(job->cached_zone_file,cached_zone_file,sizeof(job->cached_zone_file));
+ job->zone_dict = (struct AnodeDictionary *)malloc(sizeof(struct AnodeDictionary));
+ AnodeDictionary_init(job->zone_dict,0);
+ job->zone.bits[0] = zone->bits[0];
+ job->zone.bits[1] = zone->bits[1];
+ job->zone.bits[2] = zone->bits[2];
+ job->zone.bits[3] = zone->bits[3];
+ job->ptr = ptr;
+ job->zone_lookup_handler = zone_lookup_handler;
+ job->had_cached_zone = 0;
+
+ client = AnodeHttpClient_new(transport);
+
+ Anode_str_copy(client->uri.scheme,"http",sizeof(client->uri.scheme));
+ snprintf(client->uri.host,sizeof(client->uri.host),"a--%.2x%.2x%.2x%.2x.net",(unsigned int)zone->bits[0],(unsigned int)zone->bits[1],(unsigned int)zone->bits[2],(unsigned int)zone->bits[3]);
+ client->uri.host[sizeof(client->uri.host)-1] = (char)0;
+ Anode_str_copy(client->uri.path,"/z",sizeof(client->uri.path));
+
+ client->handler = &AnodeZone_lookup_http_handler;
+ client->ptr[0] = job;
+
+ if ((file_size = get_file_time_for_http(cached_zone_file,if_modified_since,sizeof(if_modified_since)))) {
+ zf = fopen(cached_zone_file,"r");
+ if (zf) {
+ AnodeDictionary_put(&client->headers,"If-Modified-Since",if_modified_since);
+ file_data = (char *)malloc(file_size + 1);
+ if (fread((void *)file_data,1,file_size,zf)) {
+ file_data[file_size] = (char)0;
+ AnodeDictionary_read(
+ job->zone_dict,
+ file_data,
+ "\r\n",
+ "=",
+ ";",
+ '\\',
+ 1,1);
+ job->had_cached_zone = 1;
+ }
+ free((void *)file_data);
+ fclose(zf);
+ }
+ }
+
+ AnodeHttpClient_send(client);
+ } else zone_lookup_handler(ptr,zone,(AnodeZone *)0);
+}
+
+const char *AnodeZoneFile_get(AnodeZoneFile *zone,const char *key)
+{
+ return AnodeDictionary_get((struct AnodeDictionary *)zone,key);
+}
+
+void AnodeZoneFile_free(AnodeZoneFile *zone)
+{
+ AnodeDictionary_destroy((struct AnodeDictionary *)zone);
+ free((void *)zone);
+}
diff --git a/attic/historic/anode/libspark/Makefile b/attic/historic/anode/libspark/Makefile
new file mode 100644
index 00000000..0d3fedd8
--- /dev/null
+++ b/attic/historic/anode/libspark/Makefile
@@ -0,0 +1,16 @@
+SYSNAME:=${shell uname}
+SYSNAME!=uname
+include ../config.mk.${SYSNAME}
+
+LIBSPARK_OBJS=
+
+all: libspark
+
+libspark: $(LIBSPARK_OBJS)
+ ar rcs libspark.a $(LIBSPARK_OBJS)
+ ranlib libspark.a
+
+clean: force
+ rm -f *.a *.so *.dylib *.dll *.lib *.exe *.o
+
+force: ;
diff --git a/attic/historic/anode/libspark/experiments/FindGoodSegmentDelimiters.cpp b/attic/historic/anode/libspark/experiments/FindGoodSegmentDelimiters.cpp
new file mode 100644
index 00000000..9b1ecaa1
--- /dev/null
+++ b/attic/historic/anode/libspark/experiments/FindGoodSegmentDelimiters.cpp
@@ -0,0 +1,161 @@
+// Searches for good delimiters to cut streams into relatively well sized
+// segments.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+#include <boost/cstdint.hpp>
+#include <boost/array.hpp>
+#include <boost/random/mersenne_twister.hpp>
+#include <boost/thread.hpp>
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <iostream>
+#include <vector>
+#include <map>
+
+// Desired size range
+#define MIN_DESIRED_SIZE 4096
+#define MAX_DESIRED_SIZE 131072
+
+#define DELIMITER_SET_SIZE 1
+typedef boost::array<boost::uint16_t,DELIMITER_SET_SIZE> DelimArray;
+
+struct BestEntry
+{
+ DelimArray best;
+ double bestScore;
+ std::vector<unsigned char> data;
+};
+
+boost::mutex bestLock;
+boost::mutex outLock;
+std::map<std::string,BestEntry> best;
+
+static void runThread(const std::string &fileName)
+{
+ char tmp[4096];
+
+ boost::mt19937 prng;
+ {
+ boost::uint32_t seed;
+ FILE *ur = fopen("/dev/urandom","r");
+ fread((void *)&seed,1,sizeof(seed),ur);
+ fclose(ur);
+ prng.seed(seed);
+ }
+
+ BestEntry *myEntry;
+ {
+ boost::mutex::scoped_lock l(bestLock);
+ myEntry = &(best[fileName]);
+ myEntry->bestScore = 99999999.0;
+ }
+
+ {
+ boost::mutex::scoped_lock l(outLock);
+
+ std::cout << "*** Reading test data from: " << fileName << std::endl;
+ FILE *f = fopen(fileName.c_str(),"r");
+ if (f) {
+ int n;
+ while ((n = fread((void *)tmp,1,sizeof(tmp),f)) > 0) {
+ for(int i=0;i<n;++i)
+ myEntry->data.push_back((unsigned char)tmp[i]);
+ }
+ fclose(f);
+ }
+
+ if (myEntry->data.size() <= 0) {
+ std::cout << "Error: no data read." << std::endl;
+ exit(1);
+ } else std::cout << "*** Read " << myEntry->data.size() << " bytes of test data." << std::endl;
+
+ std::cout.flush();
+ }
+
+ DelimArray current;
+ for(unsigned int i=0;i<DELIMITER_SET_SIZE;++i)
+ current[i] = (boost::uint16_t)prng();
+
+ for(;;) {
+ unsigned long numTooShort = 0;
+ unsigned long numTooLong = 0;
+ unsigned long numGood = 0;
+
+ boost::uint32_t shiftRegister = 0;
+ unsigned long segSize = 0;
+ for(std::vector<unsigned char>::iterator i=myEntry->data.begin();i!=myEntry->data.end();++i) {
+ shiftRegister <<= 1;
+ shiftRegister |= (((boost::uint32_t)*i) & 1);
+
+ ++segSize;
+
+ boost::uint16_t transformedShiftRegister = (boost::uint16_t)(shiftRegister);
+
+ for(DelimArray::iterator d=current.begin();d!=current.end();++d) {
+ if (transformedShiftRegister == *d) {
+ if (segSize < MIN_DESIRED_SIZE)
+ ++numTooShort;
+ else if (segSize > MAX_DESIRED_SIZE)
+ ++numTooLong;
+ else ++numGood;
+ segSize = 0;
+ break;
+ }
+ }
+ }
+ if (segSize) {
+ if (segSize < MIN_DESIRED_SIZE)
+ ++numTooShort;
+ else if (segSize > MAX_DESIRED_SIZE)
+ ++numTooLong;
+ else ++numGood;
+ }
+
+ if (numGood) {
+ double score = ((double)(numTooShort + numTooLong)) / ((double)numGood);
+
+ if (score < myEntry->bestScore) {
+ myEntry->best = current;
+ myEntry->bestScore = score;
+
+ boost::mutex::scoped_lock l(outLock);
+
+ std::cout << fileName << ": ";
+
+ for(DelimArray::iterator d=current.begin();d!=current.end();++d) {
+ sprintf(tmp,"0x%.4x",(unsigned int)*d);
+ if (d != current.begin())
+ std::cout << ',';
+ std::cout << tmp;
+ }
+
+ std::cout << ": " << numTooShort << " / " << numGood << " / " << numTooLong << " (" << score << ")" << std::endl;
+ std::cout.flush();
+
+ if ((numTooShort == 0)&&(numTooLong == 0))
+ break;
+ }
+ }
+
+ for(DelimArray::iterator i=current.begin();i!=current.end();++i)
+ *i = (boost::uint16_t)prng();
+ }
+}
+
+int main(int argc,char **argv)
+{
+ std::vector< boost::shared_ptr<boost::thread> > threads;
+
+ for(int i=1;i<argc;++i) {
+ boost::shared_ptr<boost::thread> t(new boost::thread(boost::bind(&runThread,std::string(argv[i]))));
+ threads.push_back(t);
+ }
+
+ for(std::vector< boost::shared_ptr<boost::thread> >::iterator i=threads.begin();i!=threads.end();++i)
+ (*i)->join();
+
+ return 0;
+}
diff --git a/attic/historic/anode/libspark/experiments/Makefile b/attic/historic/anode/libspark/experiments/Makefile
new file mode 100644
index 00000000..83aa4f67
--- /dev/null
+++ b/attic/historic/anode/libspark/experiments/Makefile
@@ -0,0 +1,5 @@
+all:
+ g++ -O6 -ftree-vectorize -o FindGoodSegmentDelimiters FindGoodSegmentDelimiters.cpp -lboost_thread -lpthread
+
+clean:
+ rm FindGoodSegmentDelimiters
diff --git a/attic/historic/anode/libspark/streamencoder.h b/attic/historic/anode/libspark/streamencoder.h
new file mode 100644
index 00000000..b487ca40
--- /dev/null
+++ b/attic/historic/anode/libspark/streamencoder.h
@@ -0,0 +1,108 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _SPARK_STREAMENCODER_H
+#define _SPARK_STREAMENCODER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct
+{
+ unsigned char *input_buf;
+ unsigned long input_buf_capacity;
+ unsigned long input_length;
+
+ unsigned char *stream_out_buf;
+ unsigned long stream_out_buf_capacity;
+ unsigned long stream_out_length;
+
+ void (*data_segment_add_func)(const void *data,unsigned long len,const void *global_hash,unsigned long global_hash_len);
+} SparkStreamEncoder;
+
+/**
+ * Initialize a spark stream encoder
+ *
+ * @param enc Encoder structure to initialize
+ * @param data_segment_add_func Function to call to store or cache data
+ */
+void SparkStreamEncoder_init(
+ SparkStreamEncoder *enc,
+ void (*data_segment_add_func)(
+ const void *data,
+ unsigned long len,
+ const void *global_hash,
+ unsigned long global_hash_len));
+
+/**
+ * Clean up a spark stream encoder structure
+ *
+ * @param enc Structure to clear
+ */
+void SparkStreamEncoder_destroy(SparkStreamEncoder *enc);
+
+/**
+ * Add data to encode
+ *
+ * @param enc Encoder structure
+ * @param data Data to encode
+ * @param len Length of data in bytes
+ * @return Number of bytes of result stream now available
+ */
+unsigned long SparkStreamEncoder_put(
+ SparkStreamEncoder *enc,
+ const void *data,
+ unsigned long len);
+
+/**
+ * Flush all data currently in input buffer
+ *
+ * @param enc Encoder structure to flush
+ */
+void SparkStreamEncoder_flush(SparkStreamEncoder *enc);
+
+/**
+ * @return Number of bytes of output stream available
+ */
+static inline unsigned long SparkStreamEncoder_available(SparkStreamEncoder *enc)
+{
+ return enc->stream_out_length;
+}
+
+/**
+ * @return Pointer to result stream bytes (may return null if none available)
+ */
+static inline const void *SparkStreamEncoder_get(SparkStreamEncoder *enc)
+{
+ return (const void *)(enc->stream_out_buf);
+}
+
+/**
+ * @return "Consume" result stream bytes after they're read or sent
+ */
+static inline void SparkStreamEncoder_consume(SparkStreamEncoder *enc,unsigned long len)
+{
+ unsigned long i;
+ for(i=len;i<enc->stream_out_length;++i)
+ enc->stream_out_buf[i - len] = enc->stream_out_buf[i];
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/attic/historic/anode/libspark/wrapper.h b/attic/historic/anode/libspark/wrapper.h
new file mode 100644
index 00000000..eb8c593d
--- /dev/null
+++ b/attic/historic/anode/libspark/wrapper.h
@@ -0,0 +1,66 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _SPARK_WRAPPER_H
+#define _SPARK_WRAPPER_H
+
+#include <openssl/sha.h>
+#include "../libanode/aes128.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Spark uses SHA-256 with hash length 32 */
+#define SPARK_HASH_LENGTH 32
+
+// Wrap a segment for forward propagation
+static inline void Spark_wrap(void *data,unsigned long len,void *plaintext_hash_buf,void *global_hash_buf)
+{
+ unsigned char expkey[ANODE_AES128_EXP_KEY_SIZE];
+
+ SHA256((const unsigned char *)data,len,(unsigned char *)plaintext_hash_buf);
+
+ Anode_aes128_expand_key(expkey,(const unsigned char *)plaintext_hash_buf);
+ Anode_aes128_cfb_encrypt(expkey,((const unsigned char *)plaintext_hash_buf) + 16,(unsigned char *)data,len);
+
+ SHA256((const unsigned char *)data,len,(unsigned char *)global_hash_buf);
+}
+
+// Unwrap a segment and check its integrity
+static inline int Spark_unwrap(void *data,unsigned long len,const void *plaintext_hash)
+{
+ unsigned char expkey[ANODE_AES128_EXP_KEY_SIZE];
+ unsigned char check_hash[32];
+ unsigned long i;
+
+ Anode_aes128_expand_key(expkey,(const unsigned char *)plaintext_hash);
+ Anode_aes128_cfb_decrypt(expkey,((const unsigned char *)plaintext_hash) + 16,(unsigned char *)data,len);
+
+ SHA256((const unsigned char *)data,len,check_hash);
+
+ for(i=0;i<32;++i) {
+ if (check_hash[i] != ((const unsigned char *)plaintext_hash)[i])
+ return 0;
+ }
+ return 1;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/doc/ext/kubernetes/.zerotierCliSettings b/attic/kubernetes_docs/.zerotierCliSettings
index 0e7df9b6..0e7df9b6 100644
--- a/doc/ext/kubernetes/.zerotierCliSettings
+++ b/attic/kubernetes_docs/.zerotierCliSettings
diff --git a/doc/ext/kubernetes/Dockerfile b/attic/kubernetes_docs/Dockerfile
index 6437a2bb..6437a2bb 100644
--- a/doc/ext/kubernetes/Dockerfile
+++ b/attic/kubernetes_docs/Dockerfile
diff --git a/doc/ext/kubernetes/README.md b/attic/kubernetes_docs/README.md
index 482e77e5..482e77e5 100644
--- a/doc/ext/kubernetes/README.md
+++ b/attic/kubernetes_docs/README.md
diff --git a/doc/ext/kubernetes/entrypoint.sh b/attic/kubernetes_docs/entrypoint.sh
index 80cd278e..80cd278e 100644
--- a/doc/ext/kubernetes/entrypoint.sh
+++ b/attic/kubernetes_docs/entrypoint.sh
diff --git a/doc/ext/kubernetes/server.js b/attic/kubernetes_docs/server.js
index a4b08bb8..a4b08bb8 100644
--- a/doc/ext/kubernetes/server.js
+++ b/attic/kubernetes_docs/server.js
diff --git a/attic/lat_lon_to_xyz.js b/attic/lat_lon_to_xyz.js
new file mode 100644
index 00000000..692a3687
--- /dev/null
+++ b/attic/lat_lon_to_xyz.js
@@ -0,0 +1,25 @@
+'use strict'
+
+/* This is a utility to convert latitude/longitude into X,Y,Z coordinates as used by clustering. */
+
+if (process.argv.length !== 4) {
+ console.log('Usage: node lat_lon_to_xyz.js <latitude> <longitude');
+ process.exit(1);
+}
+
+var lat = parseFloat(process.argv[2])||0.0;
+var lon = parseFloat(process.argv[3])||0.0;
+
+var latRadians = lat * 0.01745329251994; // PI / 180
+var lonRadians = lon * 0.01745329251994; // PI / 180
+var cosLat = Math.cos(latRadians);
+
+console.log({
+ lat: lat,
+ lon: lon,
+ x: Math.round((-6371.0) * cosLat * Math.cos(lonRadians)),
+ y: Math.round(6371.0 * Math.sin(latRadians)),
+ z: Math.round(6371.0 * cosLat * Math.sin(lonRadians))
+});
+
+process.exit(0);
diff --git a/attic/linux-build-farm/README.md b/attic/linux-build-farm/README.md
deleted file mode 100644
index 8055eb0b..00000000
--- a/attic/linux-build-farm/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-Dockerized Linux Build Farm
-======
-
-This subfolder contains Dockerfiles and a script to build Linux packages for a variety of Linux distributions. It's also an excellent way to test your CPU fans and stress test your disk.
-
-Running `build.sh` with no arguments builds everything. You can run `build.sh` with the name of a distro (e.g. centos-7) to only build that. Both 32 and 64 bit packages are built except where no 32-bit version of the distribution exists.
-
-The `make-apt-repos.sh` and `make-rpm-repos.sh` scripts build repositories. They may require some editing for outside-of-ZeroTier use, and be careful with the apt one if you have an existing *aptly* configuration.
diff --git a/attic/linux-build-farm/amazon-2016.03/x64/Dockerfile b/attic/linux-build-farm/amazon-2016.03/x64/Dockerfile
deleted file mode 100644
index bd1a246a..00000000
--- a/attic/linux-build-farm/amazon-2016.03/x64/Dockerfile
+++ /dev/null
@@ -1,13 +0,0 @@
-#FROM ambakshi/amazon-linux:2016.03
-#MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-#RUN yum update -y
-#RUN yum install -y epel-release
-#RUN yum install -y make development-tools rpmdevtools clang gcc-c++ ruby ruby-devel
-
-#RUN gem install ronn
-
-FROM zerotier/zt1-build-amazon-2016.03-x64-base
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-ADD zt1-src.tar.gz /
diff --git a/attic/linux-build-farm/build.sh b/attic/linux-build-farm/build.sh
deleted file mode 100755
index 0eb7c5d2..00000000
--- a/attic/linux-build-farm/build.sh
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/bin/bash
-
-export PATH=/bin:/usr/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/sbin
-
-subdirs=$*
-if [ ! -n "$subdirs" ]; then
- subdirs=`find . -type d -name '*-*' -printf '%f '`
-fi
-
-if [ ! -d ./ubuntu-trusty ]; then
- echo 'Must run from linux-build-farm subfolder.'
- exit 1
-fi
-
-rm -f zt1-src.tar.gz
-cd ..
-git archive --format=tar.gz --prefix=ZeroTierOne/ -o linux-build-farm/zt1-src.tar.gz HEAD
-cd linux-build-farm
-
-# Note that --privileged is used so we can bind mount VM shares when building in a VM.
-# It has no other impact or purpose, but probably doesn't matter here in any case.
-
-for distro in $subdirs; do
- echo
- echo "--- BUILDING FOR $distro ---"
- echo
-
- cd $distro
-
- if [ -d x64 ]; then
- cd x64
- mv ../../zt1-src.tar.gz .
- docker build -t zt1-build-${distro}-x64 .
- mv zt1-src.tar.gz ../..
- cd ..
- fi
-
- if [ -d x86 ]; then
- cd x86
- mv ../../zt1-src.tar.gz .
- docker build -t zt1-build-${distro}-x86 .
- mv zt1-src.tar.gz ../..
- cd ..
- fi
-
- rm -f *.deb *.rpm
-
-# exit 0
-
- if [ ! -n "`echo $distro | grep -F debian`" -a ! -n "`echo $distro | grep -F ubuntu`" ]; then
- if [ -d x64 ]; then
- docker run --rm -v `pwd`:/artifacts --privileged -it zt1-build-${distro}-x64 /bin/bash -c 'cd /ZeroTierOne ; make redhat ; cd .. ; cp `find /root/rpmbuild -type f -name *.rpm` /artifacts ; ls -l /artifacts'
- fi
- if [ -d x86 ]; then
- docker run --rm -v `pwd`:/artifacts --privileged -it zt1-build-${distro}-x86 /bin/bash -c 'cd /ZeroTierOne ; make redhat ; cd .. ; cp `find /root/rpmbuild -type f -name *.rpm` /artifacts ; ls -l /artifacts'
- fi
- else
- if [ -d x64 ]; then
- docker run --rm -v `pwd`:/artifacts --privileged -it zt1-build-${distro}-x64 /bin/bash -c 'cd /ZeroTierOne ; make debian ; cd .. ; cp *.deb /artifacts ; ls -l /artifacts'
- fi
- if [ -d x86 ]; then
- docker run --rm -v `pwd`:/artifacts --privileged -it zt1-build-${distro}-x86 /bin/bash -c 'cd /ZeroTierOne ; make debian ; cd .. ; cp *.deb /artifacts ; ls -l /artifacts'
- fi
- fi
-
- cd ..
-done
-
-rm -f zt1-src.tar.gz
diff --git a/attic/linux-build-farm/centos-6/x64/Dockerfile b/attic/linux-build-farm/centos-6/x64/Dockerfile
deleted file mode 100644
index 2796e422..00000000
--- a/attic/linux-build-farm/centos-6/x64/Dockerfile
+++ /dev/null
@@ -1,13 +0,0 @@
-FROM centos:6
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN yum update -y
-RUN yum install -y epel-release
-RUN yum install -y make development-tools rpmdevtools clang gcc-c++ tar
-
-RUN yum install -y nodejs npm
-
-# Stop use of http-parser-devel which is installed by nodejs/npm
-RUN rm -f /usr/include/http_parser.h
-
-ADD zt1-src.tar.gz /
diff --git a/attic/linux-build-farm/centos-6/x86/Dockerfile b/attic/linux-build-farm/centos-6/x86/Dockerfile
deleted file mode 100644
index 8192d139..00000000
--- a/attic/linux-build-farm/centos-6/x86/Dockerfile
+++ /dev/null
@@ -1,13 +0,0 @@
-FROM toopher/centos-i386:centos6
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN yum update -y
-RUN yum install -y epel-release
-RUN yum install -y make development-tools rpmdevtools clang gcc-c++ tar
-
-RUN yum install -y nodejs npm
-
-# Stop use of http-parser-devel which is installed by nodejs/npm
-RUN rm -f /usr/include/http_parser.h
-
-ADD zt1-src.tar.gz /
diff --git a/attic/linux-build-farm/centos-7/x64/Dockerfile b/attic/linux-build-farm/centos-7/x64/Dockerfile
deleted file mode 100644
index 10b58402..00000000
--- a/attic/linux-build-farm/centos-7/x64/Dockerfile
+++ /dev/null
@@ -1,10 +0,0 @@
-FROM centos:7
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN yum update -y
-RUN yum install -y epel-release
-RUN yum install -y make development-tools rpmdevtools clang gcc-c++ ruby ruby-devel
-
-RUN gem install ronn
-
-ADD zt1-src.tar.gz /
diff --git a/attic/linux-build-farm/centos-7/x86/Dockerfile b/attic/linux-build-farm/centos-7/x86/Dockerfile
deleted file mode 100644
index a637a8d3..00000000
--- a/attic/linux-build-farm/centos-7/x86/Dockerfile
+++ /dev/null
@@ -1,22 +0,0 @@
-#FROM zerotier/centos7-32bit
-#MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-#RUN echo 'i686-redhat-linux' >/etc/rpm/platform
-
-#RUN yum update -y
-#RUN yum install -y make development-tools rpmdevtools http-parser-devel lz4-devel libnatpmp-devel
-
-#RUN yum install -y gcc-c++
-#RUN rpm --install --force https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm
-#RUN rpm --install --force ftp://rpmfind.net/linux/centos/6.8/os/i386/Packages/libffi-3.0.5-3.2.el6.i686.rpm
-#RUN yum install -y clang
-
-FROM zerotier/zt1-build-centos-7-x86-base
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN yum install -y ruby ruby-devel
-RUN gem install ronn
-
-#RUN rpm --erase http-parser-devel lz4-devel libnatpmp-devel
-
-ADD zt1-src.tar.gz /
diff --git a/attic/linux-build-farm/debian-jessie/x64/Dockerfile b/attic/linux-build-farm/debian-jessie/x64/Dockerfile
deleted file mode 100644
index 316c1d83..00000000
--- a/attic/linux-build-farm/debian-jessie/x64/Dockerfile
+++ /dev/null
@@ -1,12 +0,0 @@
-FROM debian:jessie
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN apt-get update
-RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.5
-
-RUN ln -sf /usr/bin/clang++-3.5 /usr/bin/clang++
-RUN ln -sf /usr/bin/clang-3.5 /usr/bin/clang
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
diff --git a/attic/linux-build-farm/debian-jessie/x86/Dockerfile b/attic/linux-build-farm/debian-jessie/x86/Dockerfile
deleted file mode 100644
index 3ad83329..00000000
--- a/attic/linux-build-farm/debian-jessie/x86/Dockerfile
+++ /dev/null
@@ -1,12 +0,0 @@
-FROM 32bit/debian:jessie
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN apt-get update
-RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.5
-
-RUN ln -sf /usr/bin/clang++-3.5 /usr/bin/clang++
-RUN ln -sf /usr/bin/clang-3.5 /usr/bin/clang
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
diff --git a/attic/linux-build-farm/debian-stretch/x64/Dockerfile b/attic/linux-build-farm/debian-stretch/x64/Dockerfile
deleted file mode 100644
index c973c2b7..00000000
--- a/attic/linux-build-farm/debian-stretch/x64/Dockerfile
+++ /dev/null
@@ -1,12 +0,0 @@
-FROM debian:stretch
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN apt-get update
-RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang
-
-#RUN ln -sf /usr/bin/clang++-3.5 /usr/bin/clang++
-#RUN ln -sf /usr/bin/clang-3.5 /usr/bin/clang
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
diff --git a/attic/linux-build-farm/debian-stretch/x86/Dockerfile b/attic/linux-build-farm/debian-stretch/x86/Dockerfile
deleted file mode 100644
index bfc7a86f..00000000
--- a/attic/linux-build-farm/debian-stretch/x86/Dockerfile
+++ /dev/null
@@ -1,12 +0,0 @@
-FROM mcandre/docker-debian-32bit:stretch
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN apt-get update
-RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang
-
-#RUN ln -sf /usr/bin/clang++-3.5 /usr/bin/clang++
-#RUN ln -sf /usr/bin/clang-3.5 /usr/bin/clang
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
diff --git a/attic/linux-build-farm/debian-wheezy/x64/Dockerfile b/attic/linux-build-farm/debian-wheezy/x64/Dockerfile
deleted file mode 100644
index 77e1c325..00000000
--- a/attic/linux-build-farm/debian-wheezy/x64/Dockerfile
+++ /dev/null
@@ -1,12 +0,0 @@
-FROM debian:wheezy
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN apt-get update
-RUN apt-get install -y build-essential debhelper ruby-ronn g++ make devscripts
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
-
-RUN mv -f /ZeroTierOne/debian/control.wheezy /ZeroTierOne/debian/control
-RUN mv -f /ZeroTierOne/debian/rules.wheezy /ZeroTierOne/debian/rules
diff --git a/attic/linux-build-farm/debian-wheezy/x86/Dockerfile b/attic/linux-build-farm/debian-wheezy/x86/Dockerfile
deleted file mode 100644
index 1f0117d2..00000000
--- a/attic/linux-build-farm/debian-wheezy/x86/Dockerfile
+++ /dev/null
@@ -1,15 +0,0 @@
-#FROM tubia/debian:wheezy
-#MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-#RUN apt-get update
-#RUN apt-get install -y build-essential debhelper ruby-ronn g++ make devscripts
-
-FROM zerotier/zt1-build-debian-wheezy-x86-base
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
-
-RUN mv -f /ZeroTierOne/debian/control.wheezy /ZeroTierOne/debian/control
-RUN mv -f /ZeroTierOne/debian/rules.wheezy /ZeroTierOne/debian/rules
diff --git a/attic/linux-build-farm/fedora-22/x64/Dockerfile b/attic/linux-build-farm/fedora-22/x64/Dockerfile
deleted file mode 100644
index 6da0a921..00000000
--- a/attic/linux-build-farm/fedora-22/x64/Dockerfile
+++ /dev/null
@@ -1,10 +0,0 @@
-FROM fedora:22
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN yum update -y
-RUN yum install -y make rpmdevtools gcc-c++ rubygem-ronn json-parser-devel lz4-devel http-parser-devel libnatpmp-devel
-
-RUN rpm --erase http-parser-devel
-RUN yum install -y rubygem-ronn ruby
-
-ADD zt1-src.tar.gz /
diff --git a/attic/linux-build-farm/fedora-22/x86/Dockerfile b/attic/linux-build-farm/fedora-22/x86/Dockerfile
deleted file mode 100644
index 3c24b844..00000000
--- a/attic/linux-build-farm/fedora-22/x86/Dockerfile
+++ /dev/null
@@ -1,19 +0,0 @@
-#FROM nickcis/fedora-32:22
-#MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-#RUN mkdir -p /etc/dnf/vars
-#RUN echo 'i386' >/etc/dnf/vars/basearch
-#RUN echo 'i386' >/etc/dnf/vars/arch
-
-#RUN yum update -y
-#RUN yum install -y make rpmdevtools gcc-c++ rubygem-ronn json-parser-devel lz4-devel http-parser-devel libnatpmp-devel
-
-FROM zerotier/zt1-build-fedora-22-x86-base
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN echo 'i686-redhat-linux' >/etc/rpm/platform
-
-RUN rpm --erase http-parser-devel
-RUN yum install -y rubygem-ronn ruby
-
-ADD zt1-src.tar.gz /
diff --git a/attic/linux-build-farm/make-apt-repos.sh b/attic/linux-build-farm/make-apt-repos.sh
deleted file mode 100755
index 7a81cc5c..00000000
--- a/attic/linux-build-farm/make-apt-repos.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/bash
-
-# This builds a series of Debian repositories for each distribution.
-
-export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin
-
-for distro in debian-* ubuntu-*; do
- if [ -n "`find ${distro} -name '*.deb' -type f`" ]; then
- arches=`ls ${distro}/*.deb | cut -d _ -f 3 | cut -d . -f 1 | xargs | sed 's/ /,/g'`
- distro_name=`echo $distro | cut -d '-' -f 2`
- echo '---' $distro / $distro_name / $arches
- aptly repo create -architectures=${arches} -comment="ZeroTier, Inc. Debian Packages" -component="main" -distribution=${distro_name} zt-release-${distro_name}
- aptly repo add zt-release-${distro_name} ${distro}/*.deb
- aptly publish repo zt-release-${distro_name} $distro_name
- fi
-done
diff --git a/attic/linux-build-farm/make-rpm-repos.sh b/attic/linux-build-farm/make-rpm-repos.sh
deleted file mode 100755
index 0ed1cfe4..00000000
--- a/attic/linux-build-farm/make-rpm-repos.sh
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/bin/bash
-
-export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin
-
-GPG_KEY=contact@zerotier.com
-
-rm -rf /tmp/zt-rpm-repo
-mkdir /tmp/zt-rpm-repo
-
-for distro in centos-* fedora-* amazon-*; do
- dname=`echo $distro | cut -d '-' -f 1`
- if [ "$dname" = "centos" ]; then
- dname=el
- fi
- if [ "$dname" = "fedora" ]; then
- dname=fc
- fi
- if [ "$dname" = "amazon" ]; then
- dname=amzn1
- fi
- dvers=`echo $distro | cut -d '-' -f 2`
-
- mkdir -p /tmp/zt-rpm-repo/$dname/$dvers
-
- cp -v $distro/*.rpm /tmp/zt-rpm-repo/$dname/$dvers
-done
-
-rpmsign --resign --key-id=$GPG_KEY --digest-algo=sha256 `find /tmp/zt-rpm-repo -type f -name '*.rpm'`
-
-for db in `find /tmp/zt-rpm-repo -mindepth 2 -maxdepth 2 -type d`; do
- createrepo --database $db
-done
-
-# Stupid RHEL stuff
-cd /tmp/zt-rpm-repo/el
-ln -sf 6 6Client
-ln -sf 6 6Workstation
-ln -sf 6 6Server
-ln -sf 6 6.0
-ln -sf 6 6.1
-ln -sf 6 6.2
-ln -sf 6 6.3
-ln -sf 6 6.4
-ln -sf 6 6.5
-ln -sf 6 6.6
-ln -sf 6 6.7
-ln -sf 6 6.8
-ln -sf 6 6.9
-ln -sf 7 7Client
-ln -sf 7 7Workstation
-ln -sf 7 7Server
-ln -sf 7 7.0
-ln -sf 7 7.1
-ln -sf 7 7.2
-ln -sf 7 7.3
-ln -sf 7 7.4
-ln -sf 7 7.5
-ln -sf 7 7.6
-ln -sf 7 7.7
-ln -sf 7 7.8
-ln -sf 7 7.9
-
-echo
-echo Repo created in /tmp/zt-rpm-repo
diff --git a/attic/linux-build-farm/ubuntu-trusty/x64/Dockerfile b/attic/linux-build-farm/ubuntu-trusty/x64/Dockerfile
deleted file mode 100644
index f84cc6e3..00000000
--- a/attic/linux-build-farm/ubuntu-trusty/x64/Dockerfile
+++ /dev/null
@@ -1,12 +0,0 @@
-FROM ubuntu:14.04
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN apt-get update
-RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.6
-
-RUN ln -sf /usr/bin/clang++-3.6 /usr/bin/clang++
-RUN ln -sf /usr/bin/clang-3.6 /usr/bin/clang
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
diff --git a/attic/linux-build-farm/ubuntu-trusty/x86/Dockerfile b/attic/linux-build-farm/ubuntu-trusty/x86/Dockerfile
deleted file mode 100644
index 6be3ae87..00000000
--- a/attic/linux-build-farm/ubuntu-trusty/x86/Dockerfile
+++ /dev/null
@@ -1,12 +0,0 @@
-FROM 32bit/ubuntu:14.04
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN apt-get update
-RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.6
-
-RUN ln -sf /usr/bin/clang++-3.6 /usr/bin/clang++
-RUN ln -sf /usr/bin/clang-3.6 /usr/bin/clang
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
diff --git a/attic/linux-build-farm/ubuntu-wily/x64/Dockerfile b/attic/linux-build-farm/ubuntu-wily/x64/Dockerfile
deleted file mode 100644
index 99b8d34c..00000000
--- a/attic/linux-build-farm/ubuntu-wily/x64/Dockerfile
+++ /dev/null
@@ -1,12 +0,0 @@
-FROM ubuntu:wily
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN apt-get update
-RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.7
-
-RUN ln -sf /usr/bin/clang++-3.7 /usr/bin/clang++
-RUN ln -sf /usr/bin/clang-3.7 /usr/bin/clang
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
diff --git a/attic/linux-build-farm/ubuntu-wily/x86/Dockerfile b/attic/linux-build-farm/ubuntu-wily/x86/Dockerfile
deleted file mode 100644
index 86ad14f2..00000000
--- a/attic/linux-build-farm/ubuntu-wily/x86/Dockerfile
+++ /dev/null
@@ -1,12 +0,0 @@
-FROM daald/ubuntu32:wily
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN apt-get update
-RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.7
-
-RUN ln -sf /usr/bin/clang++-3.7 /usr/bin/clang++
-RUN ln -sf /usr/bin/clang-3.7 /usr/bin/clang
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
diff --git a/attic/linux-build-farm/ubuntu-xenial/x64/Dockerfile b/attic/linux-build-farm/ubuntu-xenial/x64/Dockerfile
deleted file mode 100644
index fa665a0a..00000000
--- a/attic/linux-build-farm/ubuntu-xenial/x64/Dockerfile
+++ /dev/null
@@ -1,14 +0,0 @@
-FROM ubuntu:xenial
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN apt-get update
-RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.8
-
-#RUN ln -sf /usr/bin/clang++-3.8 /usr/bin/clang++
-#RUN ln -sf /usr/bin/clang-3.8 /usr/bin/clang
-
-RUN rm -f /usr/bin/clang++ /usr/bin/clang
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
diff --git a/attic/linux-build-farm/ubuntu-xenial/x86/Dockerfile b/attic/linux-build-farm/ubuntu-xenial/x86/Dockerfile
deleted file mode 100644
index d01eec9b..00000000
--- a/attic/linux-build-farm/ubuntu-xenial/x86/Dockerfile
+++ /dev/null
@@ -1,14 +0,0 @@
-FROM f69m/ubuntu32:xenial
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN apt-get update
-RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.8
-
-#RUN ln -sf /usr/bin/clang++-3.8 /usr/bin/clang++
-#RUN ln -sf /usr/bin/clang-3.8 /usr/bin/clang
-
-RUN rm -f /usr/bin/clang++ /usr/bin/clang
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
diff --git a/attic/old-controller-schema.sql b/attic/old-controller-schema.sql
deleted file mode 100644
index d1daf8d0..00000000
--- a/attic/old-controller-schema.sql
+++ /dev/null
@@ -1,112 +0,0 @@
-CREATE TABLE Config (
- k varchar(16) PRIMARY KEY NOT NULL,
- v varchar(1024) NOT NULL
-);
-
-CREATE TABLE Network (
- id char(16) PRIMARY KEY NOT NULL,
- name varchar(128) NOT NULL,
- private integer NOT NULL DEFAULT(1),
- enableBroadcast integer NOT NULL DEFAULT(1),
- allowPassiveBridging integer NOT NULL DEFAULT(0),
- multicastLimit integer NOT NULL DEFAULT(32),
- creationTime integer NOT NULL DEFAULT(0),
- revision integer NOT NULL DEFAULT(1),
- memberRevisionCounter integer NOT NULL DEFAULT(1),
- flags integer NOT NULL DEFAULT(0)
-);
-
-CREATE TABLE AuthToken (
- id integer PRIMARY KEY NOT NULL,
- networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
- authMode integer NOT NULL DEFAULT(1),
- useCount integer NOT NULL DEFAULT(0),
- maxUses integer NOT NULL DEFAULT(0),
- expiresAt integer NOT NULL DEFAULT(0),
- token varchar(256) NOT NULL
-);
-
-CREATE INDEX AuthToken_networkId_token ON AuthToken(networkId,token);
-
-CREATE TABLE Node (
- id char(10) PRIMARY KEY NOT NULL,
- identity varchar(4096) NOT NULL
-);
-
-CREATE TABLE IpAssignment (
- networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
- nodeId char(10) REFERENCES Node(id) ON DELETE CASCADE,
- type integer NOT NULL DEFAULT(0),
- ip blob(16) NOT NULL,
- ipNetmaskBits integer NOT NULL DEFAULT(0),
- ipVersion integer NOT NULL DEFAULT(4)
-);
-
-CREATE UNIQUE INDEX IpAssignment_networkId_ip ON IpAssignment (networkId, ip);
-
-CREATE INDEX IpAssignment_networkId_nodeId ON IpAssignment (networkId, nodeId);
-
-CREATE TABLE IpAssignmentPool (
- networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
- ipRangeStart blob(16) NOT NULL,
- ipRangeEnd blob(16) NOT NULL,
- ipVersion integer NOT NULL DEFAULT(4)
-);
-
-CREATE UNIQUE INDEX IpAssignmentPool_networkId_ipRangeStart ON IpAssignmentPool (networkId,ipRangeStart);
-
-CREATE TABLE Member (
- networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
- nodeId char(10) NOT NULL REFERENCES Node(id) ON DELETE CASCADE,
- authorized integer NOT NULL DEFAULT(0),
- activeBridge integer NOT NULL DEFAULT(0),
- memberRevision integer NOT NULL DEFAULT(0),
- flags integer NOT NULL DEFAULT(0),
- lastRequestTime integer NOT NULL DEFAULT(0),
- lastPowDifficulty integer NOT NULL DEFAULT(0),
- lastPowTime integer NOT NULL DEFAULT(0),
- recentHistory blob,
- PRIMARY KEY (networkId, nodeId)
-);
-
-CREATE INDEX Member_networkId_nodeId ON Member(networkId,nodeId);
-CREATE INDEX Member_networkId_activeBridge ON Member(networkId, activeBridge);
-CREATE INDEX Member_networkId_memberRevision ON Member(networkId, memberRevision);
-CREATE INDEX Member_networkId_lastRequestTime ON Member(networkId, lastRequestTime);
-
-CREATE TABLE Route (
- networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
- target blob(16) NOT NULL,
- via blob(16),
- targetNetmaskBits integer NOT NULL,
- ipVersion integer NOT NULL,
- flags integer NOT NULL,
- metric integer NOT NULL
-);
-
-CREATE INDEX Route_networkId ON Route (networkId);
-
-CREATE TABLE Rule (
- networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
- capId integer,
- ruleNo integer NOT NULL,
- ruleType integer NOT NULL DEFAULT(0),
- "addr" blob(16),
- "int1" integer,
- "int2" integer,
- "int3" integer,
- "int4" integer
-);
-
-CREATE INDEX Rule_networkId_capId ON Rule (networkId,capId);
-
-CREATE TABLE MemberTC (
- networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
- nodeId char(10) NOT NULL REFERENCES Node(id) ON DELETE CASCADE,
- tagId integer,
- tagValue integer,
- capId integer,
- capMaxCustodyChainLength integer NOT NULL DEFAULT(1)
-);
-
-CREATE INDEX MemberTC_networkId_nodeId ON MemberTC (networkId,nodeId);
diff --git a/attic/old-linux-installer/buildinstaller.sh b/attic/old-linux-installer/buildinstaller.sh
deleted file mode 100755
index 21f2f73e..00000000
--- a/attic/old-linux-installer/buildinstaller.sh
+++ /dev/null
@@ -1,134 +0,0 @@
-#!/bin/bash
-
-# This script builds the installer for *nix systems. Windows must do everything
-# completely differently, as usual.
-
-export PATH=/bin:/usr/bin:/sbin:/usr/sbin
-
-if [ ! -f zerotier-one ]; then
- echo "Could not find 'zerotier-one' binary, please build before running this script."
- exit 2
-fi
-
-machine=`uname -m`
-system=`uname -s`
-
-vmajor=`cat version.h | grep -F ZEROTIER_ONE_VERSION_MAJOR | cut -d ' ' -f 3`
-vminor=`cat version.h | grep -F ZEROTIER_ONE_VERSION_MINOR | cut -d ' ' -f 3`
-revision=`cat version.h | grep -F ZEROTIER_ONE_VERSION_REVISION | cut -d ' ' -f 3`
-
-if [ -z "$vmajor" -o -z "$vminor" -o -z "$revision" ]; then
- echo "Unable to extract version info from version.h, aborting installer build."
- exit 2
-fi
-
-rm -rf build-installer
-mkdir build-installer
-
-case "$system" in
-
- Linux)
- # Canonicalize $machine for some architectures... we use x86
- # and x64 for Intel stuff. ARM and others should be fine if
- # we ever ship officially for those.
- debian_arch=$machine
- case "$machine" in
- i386|i486|i586|i686)
- machine="x86"
- debian_arch="i386"
- ;;
- x86_64|amd64|x64)
- machine="x64"
- debian_arch="amd64"
- ;;
- armv6l|arm|armhf|arm7l|armv7l)
- machine="armv6l"
- debian_arch="armhf"
- ;;
- esac
-
- echo "Assembling Linux installer for $machine and version $vmajor.$vminor.$revision"
-
- mkdir -p 'build-installer/var/lib/zerotier-one/ui'
- cp -fp 'ext/installfiles/linux/uninstall.sh' 'build-installer/var/lib/zerotier-one'
- cp -fp 'zerotier-one' 'build-installer/var/lib/zerotier-one'
- for f in ui/*.html ui/*.js ui/*.css ui/*.jsx ; do
- cp -fp "$f" 'build-installer/var/lib/zerotier-one/ui'
- done
- mkdir -p 'build-installer/tmp'
- cp -fp 'ext/installfiles/linux/init.d/zerotier-one' 'build-installer/tmp/init.d_zerotier-one'
- cp -fp 'ext/installfiles/linux/systemd/zerotier-one.service' 'build-installer/tmp/systemd_zerotier-one.service'
-
- targ="ZeroTierOneInstaller-linux-${machine}-${vmajor}_${vminor}_${revision}"
- # Use gzip in Linux since some minimal Linux systems do not have bunzip2
- rm -f build-installer-tmp.tar.gz
- cd build-installer
- tar -cf - * | gzip -9 >../build-installer-tmp.tar.gz
- cd ..
- rm -f $targ
- cat ext/installfiles/linux/install.tmpl.sh build-installer-tmp.tar.gz >$targ
- chmod 0755 $targ
- rm -f build-installer-tmp.tar.gz
- ls -l $targ
-
- if [ -f /usr/bin/dpkg-deb -a "$UID" -eq 0 ]; then
- echo
- echo Found dpkg-deb and you are root, trying to build Debian package.
-
- rm -rf build-installer-deb
-
- debbase="build-installer-deb/zerotier-one_${vmajor}.${vminor}.${revision}_$debian_arch"
- debfolder="${debbase}/DEBIAN"
- mkdir -p $debfolder
-
- cat 'ext/installfiles/linux/DEBIAN/control.in' | sed "s/__VERSION__/${vmajor}.${vminor}.${revision}/" | sed "s/__ARCH__/${debian_arch}/" >$debfolder/control
- cat $debfolder/control
- cp -f 'ext/installfiles/linux/DEBIAN/conffiles' "${debfolder}/conffiles"
-
- mkdir -p "${debbase}/var/lib/zerotier-one/updates.d"
- cp -f $targ "${debbase}/var/lib/zerotier-one/updates.d"
-
- rm -f "${debfolder}/postinst" "${debfolder}/prerm"
-
- echo '#!/bin/bash' >${debfolder}/postinst
- echo "/var/lib/zerotier-one/updates.d/${targ} >>/dev/null 2>&1" >>${debfolder}/postinst
- echo "/bin/rm -f /var/lib/zerotier-one/updates.d/*" >>${debfolder}/postinst
- chmod a+x ${debfolder}/postinst
-
- echo '#!/bin/bash' >${debfolder}/prerm
- echo 'export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin' >>${debfolder}/prerm
- echo 'if [ "$1" != "upgrade" ]; then' >>${debfolder}/prerm
- echo ' /var/lib/zerotier-one/uninstall.sh >>/dev/null 2>&1' >>${debfolder}/prerm
- echo 'fi' >>${debfolder}/prerm
- chmod a+x ${debfolder}/prerm
-
- dpkg-deb --build $debbase
-
- mv -f build-installer-deb/*.deb .
- rm -rf build-installer-deb
- fi
-
- if [ -f /usr/bin/rpmbuild ]; then
- echo
- echo Found rpmbuild, trying to build RedHat/CentOS package.
-
- rm -f /tmp/zerotier-one.spec
- curr_dir=`pwd`
- cat ext/installfiles/linux/RPM/zerotier-one.spec.in | sed "s/__VERSION__/${vmajor}.${vminor}.${revision}/g" | sed "s/__INSTALLER__/${targ}/g" >/tmp/zerotier-one.spec
-
- rpmbuild -ba /tmp/zerotier-one.spec
-
- rm -f /tmp/zerotier-one.spec
- fi
-
- ;;
-
- *)
- echo "Unsupported platform: $system"
- exit 2
-
-esac
-
-rm -rf build-installer
-
-exit 0
diff --git a/attic/old-linux-installer/install.tmpl.sh b/attic/old-linux-installer/install.tmpl.sh
deleted file mode 100644
index 2d18d24c..00000000
--- a/attic/old-linux-installer/install.tmpl.sh
+++ /dev/null
@@ -1,182 +0,0 @@
-#!/bin/bash
-
-export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin
-shopt -s expand_aliases
-
-dryRun=0
-
-echo "*** ZeroTier One install/update ***"
-echo
-
-if [ "$UID" -ne 0 ]; then
- echo "Not running as root so doing dry run (no modifications to system)..."
- dryRun=1
-fi
-
-if [ $dryRun -gt 0 ]; then
- alias ln="echo '>> ln'"
- alias rm="echo '>> rm'"
- alias mv="echo '>> mv'"
- alias cp="echo '>> cp'"
- alias chown="echo '>> chown'"
- alias chgrp="echo '>> chgrp'"
- alias chmod="echo '>> chmod'"
- alias chkconfig="echo '>> chkconfig'"
- alias zerotier-cli="echo '>> zerotier-cli'"
- alias service="echo '>> service'"
- alias systemctl="echo '>> systemctl'"
-fi
-
-scriptPath="`dirname "$0"`/`basename "$0"`"
-if [ ! -r "$scriptPath" ]; then
- scriptPath="$0"
- if [ ! -r "$scriptPath" ]; then
- echo "Installer cannot determine its own path; $scriptPath is not readable."
- exit 2
- fi
-fi
-
-# Check for systemd vs. old school SysV init
-SYSTEMDUNITDIR=
-if [ -e /bin/systemctl -o -e /usr/bin/systemctl -o -e /usr/local/bin/systemctl -o -e /sbin/systemctl -o -e /usr/sbin/systemctl ]; then
- # Second check: test if systemd appears to actually be running. Apparently Ubuntu
- # thought it was a good idea to ship with systemd installed but not used. Issue #133
- if [ -d /var/run/systemd/system -o -d /run/systemd/system ]; then
- if [ -e /usr/bin/pkg-config ]; then
- SYSTEMDUNITDIR=`/usr/bin/pkg-config systemd --variable=systemdsystemunitdir`
- fi
- if [ -z "$SYSTEMDUNITDIR" -o ! -d "$SYSTEMDUNITDIR" ]; then
- if [ -d /usr/lib/systemd/system ]; then
- SYSTEMDUNITDIR=/usr/lib/systemd/system
- fi
- if [ -d /etc/systemd/system ]; then
- SYSTEMDUNITDIR=/etc/systemd/system
- fi
- fi
- fi
-fi
-
-# Find the end of this script, which is where we have appended binary data.
-endMarkerIndex=`grep -a -b -E '^################' "$scriptPath" | head -c 16 | cut -d : -f 1`
-if [ "$endMarkerIndex" -le 100 ]; then
- echo 'Internal error: unable to find end of script / start of binary data marker.'
- exit 2
-fi
-blobStart=`expr $endMarkerIndex + 17`
-if [ "$blobStart" -le "$endMarkerIndex" ]; then
- echo 'Internal error: unable to find end of script / start of binary data marker.'
- exit 2
-fi
-
-echo -n 'Getting version of existing install... '
-origVersion=NONE
-if [ -x /var/lib/zerotier-one/zerotier-one ]; then
- origVersion=`/var/lib/zerotier-one/zerotier-one -v`
-fi
-echo $origVersion
-
-echo 'Extracting files...'
-if [ $dryRun -gt 0 ]; then
- echo ">> tail -c +$blobStart \"$scriptPath\" | gunzip -c | tar -xvop -C / -f -"
- tail -c +$blobStart "$scriptPath" | gunzip -c | tar -t -f - | sed 's/^/>> /'
-else
- tail -c +$blobStart "$scriptPath" | gunzip -c | tar -xvop --no-overwrite-dir -C / -f -
-fi
-
-if [ $dryRun -eq 0 -a ! -x "/var/lib/zerotier-one/zerotier-one" ]; then
- echo 'Archive extraction failed, cannot find zerotier-one binary in "/var/lib/zerotier-one".'
- exit 2
-fi
-
-echo -n 'Getting version of new install... '
-newVersion=`/var/lib/zerotier-one/zerotier-one -v`
-echo $newVersion
-
-echo 'Creating symlinks...'
-
-rm -f /usr/bin/zerotier-cli /usr/bin/zerotier-idtool
-ln -sf /var/lib/zerotier-one/zerotier-one /usr/bin/zerotier-cli
-ln -sf /var/lib/zerotier-one/zerotier-one /usr/bin/zerotier-idtool
-
-echo 'Installing zerotier-one service...'
-
-if [ -n "$SYSTEMDUNITDIR" -a -d "$SYSTEMDUNITDIR" ]; then
- # SYSTEMD
-
- # If this was updated or upgraded from an init.d based system, clean up the old
- # init.d stuff before installing directly via systemd.
- if [ -f /etc/init.d/zerotier-one ]; then
- if [ -e /sbin/chkconfig -o -e /usr/sbin/chkconfig -o -e /bin/chkconfig -o -e /usr/bin/chkconfig ]; then
- chkconfig zerotier-one off
- fi
- rm -f /etc/init.d/zerotier-one
- fi
-
- cp -f /tmp/systemd_zerotier-one.service "$SYSTEMDUNITDIR/zerotier-one.service"
- chown 0 "$SYSTEMDUNITDIR/zerotier-one.service"
- chgrp 0 "$SYSTEMDUNITDIR/zerotier-one.service"
- chmod 0644 "$SYSTEMDUNITDIR/zerotier-one.service"
- rm -f /tmp/systemd_zerotier-one.service /tmp/init.d_zerotier-one
-
- systemctl enable zerotier-one.service
-
- echo
- echo 'Done! Installed and service configured to start at system boot.'
- echo
- echo "To start now or restart the service if it's already running:"
- echo ' sudo systemctl restart zerotier-one.service'
-else
- # SYSV INIT -- also covers upstart which supports SysVinit backward compatibility
-
- cp -f /tmp/init.d_zerotier-one /etc/init.d/zerotier-one
- chmod 0755 /etc/init.d/zerotier-one
- rm -f /tmp/systemd_zerotier-one.service /tmp/init.d_zerotier-one
-
- if [ -f /sbin/chkconfig -o -f /usr/sbin/chkconfig -o -f /usr/bin/chkconfig -o -f /bin/chkconfig ]; then
- chkconfig zerotier-one on
- else
- # Yes Virginia, some systems lack chkconfig.
- if [ -d /etc/rc0.d ]; then
- rm -f /etc/rc0.d/???zerotier-one
- ln -sf /etc/init.d/zerotier-one /etc/rc0.d/K89zerotier-one
- fi
- if [ -d /etc/rc1.d ]; then
- rm -f /etc/rc1.d/???zerotier-one
- ln -sf /etc/init.d/zerotier-one /etc/rc1.d/K89zerotier-one
- fi
- if [ -d /etc/rc2.d ]; then
- rm -f /etc/rc2.d/???zerotier-one
- ln -sf /etc/init.d/zerotier-one /etc/rc2.d/S11zerotier-one
- fi
- if [ -d /etc/rc3.d ]; then
- rm -f /etc/rc3.d/???zerotier-one
- ln -sf /etc/init.d/zerotier-one /etc/rc3.d/S11zerotier-one
- fi
- if [ -d /etc/rc4.d ]; then
- rm -f /etc/rc4.d/???zerotier-one
- ln -sf /etc/init.d/zerotier-one /etc/rc4.d/S11zerotier-one
- fi
- if [ -d /etc/rc5.d ]; then
- rm -f /etc/rc5.d/???zerotier-one
- ln -sf /etc/init.d/zerotier-one /etc/rc5.d/S11zerotier-one
- fi
- if [ -d /etc/rc6.d ]; then
- rm -f /etc/rc6.d/???zerotier-one
- ln -sf /etc/init.d/zerotier-one /etc/rc6.d/K89zerotier-one
- fi
- fi
-
- echo
- echo 'Done! Installed and service configured to start at system boot.'
- echo
- echo "To start now or restart the service if it's already running:"
- echo ' sudo service zerotier-one restart'
-fi
-
-exit 0
-
-# Do not remove the last line or add a carriage return to it! The installer
-# looks for an unterminated line beginning with 16 #'s in itself to find
-# the binary blob data, which is appended after it.
-
-################ \ No newline at end of file
diff --git a/attic/old-linux-installer/uninstall.sh b/attic/old-linux-installer/uninstall.sh
deleted file mode 100755
index d9495a18..00000000
--- a/attic/old-linux-installer/uninstall.sh
+++ /dev/null
@@ -1,76 +0,0 @@
-#!/bin/bash
-
-export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin
-
-if [ "$UID" -ne 0 ]; then
- echo "Must be run as root; try: sudo $0"
- exit 1
-fi
-
-# Detect systemd vs. regular init
-SYSTEMDUNITDIR=
-if [ -e /bin/systemctl -o -e /usr/bin/systemctl -o -e /usr/local/bin/systemctl -o -e /sbin/systemctl -o -e /usr/sbin/systemctl ]; then
- if [ -e /usr/bin/pkg-config ]; then
- SYSTEMDUNITDIR=`/usr/bin/pkg-config systemd --variable=systemdsystemunitdir`
- fi
- if [ -z "$SYSTEMDUNITDIR" -o ! -d "$SYSTEMDUNITDIR" ]; then
- if [ -d /usr/lib/systemd/system ]; then
- SYSTEMDUNITDIR=/usr/lib/systemd/system
- fi
- if [ -d /etc/systemd/system ]; then
- SYSTEMDUNITDIR=/etc/systemd/system
- fi
- fi
-fi
-
-echo "Killing any running zerotier-one service..."
-if [ -n "$SYSTEMDUNITDIR" -a -d "$SYSTEMDUNITDIR" ]; then
- systemctl stop zerotier-one.service
- systemctl disable zerotier-one.service
-else
- if [ -f /sbin/service -o -f /usr/sbin/service -o -f /bin/service -o -f /usr/bin/service ]; then
- service zerotier-one stop
- fi
-fi
-
-sleep 1
-if [ -f /var/lib/zerotier-one/zerotier-one.pid ]; then
- kill -TERM `cat /var/lib/zerotier-one/zerotier-one.pid`
- sleep 1
-fi
-if [ -f /var/lib/zerotier-one/zerotier-one.pid ]; then
- kill -KILL `cat /var/lib/zerotier-one/zerotier-one.pid`
-fi
-
-if [ -f /etc/init.d/zerotier-one ]; then
- echo "Removing SysV init items..."
- if [ -f /sbin/chkconfig -o -f /usr/sbin/chkconfig -o -f /bin/chkconfig -o -f /usr/bin/chkconfig ]; then
- chkconfig zerotier-one off
- fi
- rm -f /etc/init.d/zerotier-one
- find /etc/rc*.d -type f -name '???zerotier-one' -print0 | xargs -0 rm -f
-fi
-
-if [ -n "$SYSTEMDUNITDIR" -a -d "$SYSTEMDUNITDIR" -a -f "$SYSTEMDUNITDIR/zerotier-one.service" ]; then
- echo "Removing systemd service..."
- rm -f "$SYSTEMDUNITDIR/zerotier-one.service"
-fi
-
-echo "Erasing binary and support files..."
-if [ -d /var/lib/zerotier-one ]; then
- cd /var/lib/zerotier-one
- rm -rf zerotier-one *.persist identity.public *.log *.pid *.sh updates.d networks.d iddb.d root-topology ui
-fi
-
-echo "Erasing anything installed into system bin directories..."
-rm -f /usr/local/bin/zerotier-cli /usr/bin/zerotier-cli /usr/local/bin/zerotier-idtool /usr/bin/zerotier-idtool
-
-echo "Done."
-echo
-echo "Your ZeroTier One identity is still preserved in /var/lib/zerotier-one"
-echo "as identity.secret and can be manually deleted if you wish. Save it if"
-echo "you wish to re-use the address of this node, as it cannot be regenerated."
-
-echo
-
-exit 0
diff --git a/controller/DB.cpp b/controller/DB.cpp
new file mode 100644
index 00000000..b2e8878a
--- /dev/null
+++ b/controller/DB.cpp
@@ -0,0 +1,511 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "DB.hpp"
+#include "EmbeddedNetworkController.hpp"
+
+#include <chrono>
+#include <algorithm>
+#include <stdexcept>
+
+using json = nlohmann::json;
+
+namespace ZeroTier {
+
+void DB::initNetwork(nlohmann::json &network)
+{
+ if (!network.count("private")) network["private"] = true;
+ if (!network.count("creationTime")) network["creationTime"] = OSUtils::now();
+ if (!network.count("name")) network["name"] = "";
+ if (!network.count("multicastLimit")) network["multicastLimit"] = (uint64_t)32;
+ if (!network.count("enableBroadcast")) network["enableBroadcast"] = true;
+ if (!network.count("v4AssignMode")) network["v4AssignMode"] = {{"zt",false}};
+ if (!network.count("v6AssignMode")) network["v6AssignMode"] = {{"rfc4193",false},{"zt",false},{"6plane",false}};
+ if (!network.count("authTokens")) network["authTokens"] = {{}};
+ if (!network.count("capabilities")) network["capabilities"] = nlohmann::json::array();
+ if (!network.count("tags")) network["tags"] = nlohmann::json::array();
+ if (!network.count("routes")) network["routes"] = nlohmann::json::array();
+ if (!network.count("ipAssignmentPools")) network["ipAssignmentPools"] = nlohmann::json::array();
+ if (!network.count("mtu")) network["mtu"] = ZT_DEFAULT_MTU;
+ if (!network.count("remoteTraceTarget")) network["remoteTraceTarget"] = nlohmann::json();
+ if (!network.count("removeTraceLevel")) network["remoteTraceLevel"] = 0;
+ if (!network.count("rules")) {
+ // If unspecified, rules are set to allow anything and behave like a flat L2 segment
+ network["rules"] = {{
+ { "not",false },
+ { "or", false },
+ { "type","ACTION_ACCEPT" }
+ }};
+ }
+ network["objtype"] = "network";
+}
+
+void DB::initMember(nlohmann::json &member)
+{
+ if (!member.count("authorized")) member["authorized"] = false;
+ if (!member.count("ipAssignments")) member["ipAssignments"] = nlohmann::json::array();
+ if (!member.count("activeBridge")) member["activeBridge"] = false;
+ if (!member.count("tags")) member["tags"] = nlohmann::json::array();
+ if (!member.count("capabilities")) member["capabilities"] = nlohmann::json::array();
+ if (!member.count("creationTime")) member["creationTime"] = OSUtils::now();
+ if (!member.count("noAutoAssignIps")) member["noAutoAssignIps"] = false;
+ if (!member.count("revision")) member["revision"] = 0ULL;
+ if (!member.count("lastDeauthorizedTime")) member["lastDeauthorizedTime"] = 0ULL;
+ if (!member.count("lastAuthorizedTime")) member["lastAuthorizedTime"] = 0ULL;
+ if (!member.count("lastAuthorizedCredentialType")) member["lastAuthorizedCredentialType"] = nlohmann::json();
+ if (!member.count("lastAuthorizedCredential")) member["lastAuthorizedCredential"] = nlohmann::json();
+ if (!member.count("vMajor")) member["vMajor"] = -1;
+ if (!member.count("vMinor")) member["vMinor"] = -1;
+ if (!member.count("vRev")) member["vRev"] = -1;
+ if (!member.count("vProto")) member["vProto"] = -1;
+ if (!member.count("remoteTraceTarget")) member["remoteTraceTarget"] = nlohmann::json();
+ if (!member.count("removeTraceLevel")) member["remoteTraceLevel"] = 0;
+ member["objtype"] = "member";
+}
+
+void DB::cleanNetwork(nlohmann::json &network)
+{
+ network.erase("clock");
+ network.erase("authorizedMemberCount");
+ network.erase("activeMemberCount");
+ network.erase("totalMemberCount");
+ network.erase("lastModified");
+}
+
+void DB::cleanMember(nlohmann::json &member)
+{
+ member.erase("clock");
+ member.erase("physicalAddr");
+ member.erase("recentLog");
+ member.erase("lastModified");
+ member.erase("lastRequestMetaData");
+}
+
+DB::DB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path) :
+ _controller(nc),
+ _myId(myId),
+ _myAddress(myId.address()),
+ _path((path) ? path : "")
+{
+ char tmp[32];
+ _myAddress.toString(tmp);
+ _myAddressStr = tmp;
+}
+
+DB::~DB()
+{
+}
+
+bool DB::get(const uint64_t networkId,nlohmann::json &network)
+{
+ waitForReady();
+ std::shared_ptr<_Network> nw;
+ {
+ std::lock_guard<std::mutex> l(_networks_l);
+ auto nwi = _networks.find(networkId);
+ if (nwi == _networks.end())
+ return false;
+ nw = nwi->second;
+ }
+ {
+ std::lock_guard<std::mutex> l2(nw->lock);
+ network = nw->config;
+ }
+ return true;
+}
+
+bool DB::get(const uint64_t networkId,nlohmann::json &network,const uint64_t memberId,nlohmann::json &member)
+{
+ waitForReady();
+ std::shared_ptr<_Network> nw;
+ {
+ std::lock_guard<std::mutex> l(_networks_l);
+ auto nwi = _networks.find(networkId);
+ if (nwi == _networks.end())
+ return false;
+ nw = nwi->second;
+ }
+ {
+ std::lock_guard<std::mutex> l2(nw->lock);
+ network = nw->config;
+ auto m = nw->members.find(memberId);
+ if (m == nw->members.end())
+ return false;
+ member = m->second;
+ }
+ return true;
+}
+
+bool DB::get(const uint64_t networkId,nlohmann::json &network,const uint64_t memberId,nlohmann::json &member,NetworkSummaryInfo &info)
+{
+ waitForReady();
+ std::shared_ptr<_Network> nw;
+ {
+ std::lock_guard<std::mutex> l(_networks_l);
+ auto nwi = _networks.find(networkId);
+ if (nwi == _networks.end())
+ return false;
+ nw = nwi->second;
+ }
+ {
+ std::lock_guard<std::mutex> l2(nw->lock);
+ network = nw->config;
+ _fillSummaryInfo(nw,info);
+ auto m = nw->members.find(memberId);
+ if (m == nw->members.end())
+ return false;
+ member = m->second;
+ }
+ return true;
+}
+
+bool DB::get(const uint64_t networkId,nlohmann::json &network,std::vector<nlohmann::json> &members)
+{
+ waitForReady();
+ std::shared_ptr<_Network> nw;
+ {
+ std::lock_guard<std::mutex> l(_networks_l);
+ auto nwi = _networks.find(networkId);
+ if (nwi == _networks.end())
+ return false;
+ nw = nwi->second;
+ }
+ {
+ std::lock_guard<std::mutex> l2(nw->lock);
+ network = nw->config;
+ for(auto m=nw->members.begin();m!=nw->members.end();++m)
+ members.push_back(m->second);
+ }
+ return true;
+}
+
+bool DB::summary(const uint64_t networkId,NetworkSummaryInfo &info)
+{
+ waitForReady();
+ std::shared_ptr<_Network> nw;
+ {
+ std::lock_guard<std::mutex> l(_networks_l);
+ auto nwi = _networks.find(networkId);
+ if (nwi == _networks.end())
+ return false;
+ nw = nwi->second;
+ }
+ {
+ std::lock_guard<std::mutex> l2(nw->lock);
+ _fillSummaryInfo(nw,info);
+ }
+ return true;
+}
+
+void DB::networks(std::vector<uint64_t> &networks)
+{
+ waitForReady();
+ std::lock_guard<std::mutex> l(_networks_l);
+ networks.reserve(_networks.size() + 1);
+ for(auto n=_networks.begin();n!=_networks.end();++n)
+ networks.push_back(n->first);
+}
+
+void DB::_memberChanged(nlohmann::json &old,nlohmann::json &memberConfig,bool push)
+{
+ uint64_t memberId = 0;
+ uint64_t networkId = 0;
+ bool isAuth = false;
+ bool wasAuth = false;
+ std::shared_ptr<_Network> nw;
+
+ if (old.is_object()) {
+ memberId = OSUtils::jsonIntHex(old["id"],0ULL);
+ networkId = OSUtils::jsonIntHex(old["nwid"],0ULL);
+ if ((memberId)&&(networkId)) {
+ {
+ std::lock_guard<std::mutex> l(_networks_l);
+ auto nw2 = _networks.find(networkId);
+ if (nw2 != _networks.end())
+ nw = nw2->second;
+ }
+ if (nw) {
+ std::lock_guard<std::mutex> l(nw->lock);
+ if (OSUtils::jsonBool(old["activeBridge"],false))
+ nw->activeBridgeMembers.erase(memberId);
+ wasAuth = OSUtils::jsonBool(old["authorized"],false);
+ if (wasAuth)
+ nw->authorizedMembers.erase(memberId);
+ json &ips = old["ipAssignments"];
+ if (ips.is_array()) {
+ for(unsigned long i=0;i<ips.size();++i) {
+ json &ipj = ips[i];
+ if (ipj.is_string()) {
+ const std::string ips = ipj;
+ InetAddress ipa(ips.c_str());
+ ipa.setPort(0);
+ nw->allocatedIps.erase(ipa);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (memberConfig.is_object()) {
+ if (!nw) {
+ memberId = OSUtils::jsonIntHex(memberConfig["id"],0ULL);
+ networkId = OSUtils::jsonIntHex(memberConfig["nwid"],0ULL);
+ if ((!memberId)||(!networkId))
+ return;
+ std::lock_guard<std::mutex> l(_networks_l);
+ std::shared_ptr<_Network> &nw2 = _networks[networkId];
+ if (!nw2)
+ nw2.reset(new _Network);
+ nw = nw2;
+ }
+
+ {
+ std::lock_guard<std::mutex> l(nw->lock);
+
+ nw->members[memberId] = memberConfig;
+
+ if (OSUtils::jsonBool(memberConfig["activeBridge"],false))
+ nw->activeBridgeMembers.insert(memberId);
+ isAuth = OSUtils::jsonBool(memberConfig["authorized"],false);
+ if (isAuth)
+ nw->authorizedMembers.insert(memberId);
+ json &ips = memberConfig["ipAssignments"];
+ if (ips.is_array()) {
+ for(unsigned long i=0;i<ips.size();++i) {
+ json &ipj = ips[i];
+ if (ipj.is_string()) {
+ const std::string ips = ipj;
+ InetAddress ipa(ips.c_str());
+ ipa.setPort(0);
+ nw->allocatedIps.insert(ipa);
+ }
+ }
+ }
+
+ if (!isAuth) {
+ const int64_t ldt = (int64_t)OSUtils::jsonInt(memberConfig["lastDeauthorizedTime"],0ULL);
+ if (ldt > nw->mostRecentDeauthTime)
+ nw->mostRecentDeauthTime = ldt;
+ }
+ }
+
+ if (push)
+ _controller->onNetworkMemberUpdate(networkId,memberId);
+ } else if (memberId) {
+ if (nw) {
+ std::lock_guard<std::mutex> l(nw->lock);
+ nw->members.erase(memberId);
+ }
+ if (networkId) {
+ std::lock_guard<std::mutex> l(_networks_l);
+ auto er = _networkByMember.equal_range(memberId);
+ for(auto i=er.first;i!=er.second;++i) {
+ if (i->second == networkId) {
+ _networkByMember.erase(i);
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ if (old.is_object()) {
+ json &config = old["config"];
+ if (config.is_object()) {
+ memberId = OSUtils::jsonIntHex(config["id"],0ULL);
+ networkId = OSUtils::jsonIntHex(config["nwid"],0ULL);
+ if ((memberId)&&(networkId)) {
+ {
+ std::lock_guard<std::mutex> l(_networks_l);
+ auto nw2 = _networks.find(networkId);
+ if (nw2 != _networks.end())
+ nw = nw2->second;
+ }
+ if (nw) {
+ std::lock_guard<std::mutex> l(nw->lock);
+ if (OSUtils::jsonBool(config["activeBridge"],false))
+ nw->activeBridgeMembers.erase(memberId);
+ wasAuth = OSUtils::jsonBool(config["authorized"],false);
+ if (wasAuth)
+ nw->authorizedMembers.erase(memberId);
+ json &ips = config["ipAssignments"];
+ if (ips.is_array()) {
+ for(unsigned long i=0;i<ips.size();++i) {
+ json &ipj = ips[i];
+ if (ipj.is_string()) {
+ const std::string ips = ipj;
+ InetAddress ipa(ips.c_str());
+ ipa.setPort(0);
+ nw->allocatedIps.erase(ipa);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (member.is_object()) {
+ json &config = member["config"];
+ if (config.is_object()) {
+ if (!nw) {
+ memberId = OSUtils::jsonIntHex(config["id"],0ULL);
+ networkId = OSUtils::jsonIntHex(config["nwid"],0ULL);
+ if ((!memberId)||(!networkId))
+ return;
+ std::lock_guard<std::mutex> l(_networks_l);
+ std::shared_ptr<_Network> &nw2 = _networks[networkId];
+ if (!nw2)
+ nw2.reset(new _Network);
+ nw = nw2;
+ }
+
+ {
+ std::lock_guard<std::mutex> l(nw->lock);
+
+ nw->members[memberId] = config;
+
+ if (OSUtils::jsonBool(config["activeBridge"],false))
+ nw->activeBridgeMembers.insert(memberId);
+ isAuth = OSUtils::jsonBool(config["authorized"],false);
+ if (isAuth)
+ nw->authorizedMembers.insert(memberId);
+ json &ips = config["ipAssignments"];
+ if (ips.is_array()) {
+ for(unsigned long i=0;i<ips.size();++i) {
+ json &ipj = ips[i];
+ if (ipj.is_string()) {
+ const std::string ips = ipj;
+ InetAddress ipa(ips.c_str());
+ ipa.setPort(0);
+ nw->allocatedIps.insert(ipa);
+ }
+ }
+ }
+
+ if (!isAuth) {
+ const int64_t ldt = (int64_t)OSUtils::jsonInt(config["lastDeauthorizedTime"],0ULL);
+ if (ldt > nw->mostRecentDeauthTime)
+ nw->mostRecentDeauthTime = ldt;
+ }
+ }
+
+ if (push)
+ _controller->onNetworkMemberUpdate(networkId,memberId);
+ }
+ } else if (memberId) {
+ if (nw) {
+ std::lock_guard<std::mutex> l(nw->lock);
+ nw->members.erase(memberId);
+ }
+ if (networkId) {
+ std::lock_guard<std::mutex> l(_networks_l);
+ auto er = _networkByMember.equal_range(memberId);
+ for(auto i=er.first;i!=er.second;++i) {
+ if (i->second == networkId) {
+ _networkByMember.erase(i);
+ break;
+ }
+ }
+ }
+ }
+ */
+
+ if ((push)&&((wasAuth)&&(!isAuth)&&(networkId)&&(memberId)))
+ _controller->onNetworkMemberDeauthorize(networkId,memberId);
+}
+
+void DB::_networkChanged(nlohmann::json &old,nlohmann::json &networkConfig,bool push)
+{
+ if (networkConfig.is_object()) {
+ const std::string ids = networkConfig["id"];
+ const uint64_t id = Utils::hexStrToU64(ids.c_str());
+ if (id) {
+ std::shared_ptr<_Network> nw;
+ {
+ std::lock_guard<std::mutex> l(_networks_l);
+ std::shared_ptr<_Network> &nw2 = _networks[id];
+ if (!nw2)
+ nw2.reset(new _Network);
+ nw = nw2;
+ }
+ {
+ std::lock_guard<std::mutex> l2(nw->lock);
+ nw->config = networkConfig;
+ }
+ if (push)
+ _controller->onNetworkUpdate(id);
+ }
+ } else if (old.is_object()) {
+ const std::string ids = old["id"];
+ const uint64_t id = Utils::hexStrToU64(ids.c_str());
+ if (id) {
+ std::lock_guard<std::mutex> l(_networks_l);
+ _networks.erase(id);
+ }
+ }
+
+ /*
+ if (network.is_object()) {
+ json &config = network["config"];
+ if (networkConfig.is_object()) {
+ const std::string ids = config["id"];
+ const uint64_t id = Utils::hexStrToU64(ids.c_str());
+ if (id) {
+ std::shared_ptr<_Network> nw;
+ {
+ std::lock_guard<std::mutex> l(_networks_l);
+ std::shared_ptr<_Network> &nw2 = _networks[id];
+ if (!nw2)
+ nw2.reset(new _Network);
+ nw = nw2;
+ }
+ {
+ std::lock_guard<std::mutex> l2(nw->lock);
+ nw->config = config;
+ }
+ if (push)
+ _controller->onNetworkUpdate(id);
+ }
+ }
+ } else if (old.is_object()) {
+ const std::string ids = old["id"];
+ const uint64_t id = Utils::hexStrToU64(ids.c_str());
+ if (id) {
+ std::lock_guard<std::mutex> l(_networks_l);
+ _networks.erase(id);
+ }
+ }
+ */
+}
+
+void DB::_fillSummaryInfo(const std::shared_ptr<_Network> &nw,NetworkSummaryInfo &info)
+{
+ for(auto ab=nw->activeBridgeMembers.begin();ab!=nw->activeBridgeMembers.end();++ab)
+ info.activeBridges.push_back(Address(*ab));
+ std::sort(info.activeBridges.begin(),info.activeBridges.end());
+ for(auto ip=nw->allocatedIps.begin();ip!=nw->allocatedIps.end();++ip)
+ info.allocatedIps.push_back(*ip);
+ std::sort(info.allocatedIps.begin(),info.allocatedIps.end());
+ info.authorizedMemberCount = (unsigned long)nw->authorizedMembers.size();
+ info.totalMemberCount = (unsigned long)nw->members.size();
+ info.mostRecentDeauthTime = nw->mostRecentDeauthTime;
+}
+
+} // namespace ZeroTier
diff --git a/controller/DB.hpp b/controller/DB.hpp
new file mode 100644
index 00000000..4757bb40
--- /dev/null
+++ b/controller/DB.hpp
@@ -0,0 +1,139 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ZT_CONTROLLER_DB_HPP
+#define ZT_CONTROLLER_DB_HPP
+
+#include "../node/Constants.hpp"
+#include "../node/Identity.hpp"
+#include "../node/InetAddress.hpp"
+#include "../osdep/OSUtils.hpp"
+#include "../osdep/BlockingQueue.hpp"
+
+#include <memory>
+#include <string>
+#include <thread>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+#include <atomic>
+
+#include "../ext/json/json.hpp"
+
+#define ZT_CONTROLLER_RETHINKDB_COMMIT_THREADS 2
+
+namespace ZeroTier
+{
+
+class EmbeddedNetworkController;
+
+/**
+ * Base class with common infrastructure for all controller DB implementations
+ */
+class DB
+{
+public:
+ struct NetworkSummaryInfo
+ {
+ NetworkSummaryInfo() : authorizedMemberCount(0),totalMemberCount(0),mostRecentDeauthTime(0) {}
+ std::vector<Address> activeBridges;
+ std::vector<InetAddress> allocatedIps;
+ unsigned long authorizedMemberCount;
+ unsigned long totalMemberCount;
+ int64_t mostRecentDeauthTime;
+ };
+
+ /**
+ * Ensure that all network fields are present
+ */
+ static void initNetwork(nlohmann::json &network);
+
+ /**
+ * Ensure that all member fields are present
+ */
+ static void initMember(nlohmann::json &member);
+
+ /**
+ * Remove old and temporary network fields
+ */
+ static void cleanNetwork(nlohmann::json &network);
+
+ /**
+ * Remove old and temporary member fields
+ */
+ static void cleanMember(nlohmann::json &member);
+
+ DB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path);
+ virtual ~DB();
+
+ virtual bool waitForReady() = 0;
+
+ inline bool hasNetwork(const uint64_t networkId) const
+ {
+ std::lock_guard<std::mutex> l(_networks_l);
+ return (_networks.find(networkId) != _networks.end());
+ }
+
+ bool get(const uint64_t networkId,nlohmann::json &network);
+ bool get(const uint64_t networkId,nlohmann::json &network,const uint64_t memberId,nlohmann::json &member);
+ bool get(const uint64_t networkId,nlohmann::json &network,const uint64_t memberId,nlohmann::json &member,NetworkSummaryInfo &info);
+ bool get(const uint64_t networkId,nlohmann::json &network,std::vector<nlohmann::json> &members);
+
+ bool summary(const uint64_t networkId,NetworkSummaryInfo &info);
+
+ void networks(std::vector<uint64_t> &networks);
+
+ virtual void save(nlohmann::json *orig,nlohmann::json &record) = 0;
+
+ virtual void eraseNetwork(const uint64_t networkId) = 0;
+
+ virtual void eraseMember(const uint64_t networkId,const uint64_t memberId) = 0;
+
+ virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress) = 0;
+
+protected:
+ struct _Network
+ {
+ _Network() : mostRecentDeauthTime(0) {}
+ nlohmann::json config;
+ std::unordered_map<uint64_t,nlohmann::json> members;
+ std::unordered_set<uint64_t> activeBridgeMembers;
+ std::unordered_set<uint64_t> authorizedMembers;
+ std::unordered_set<InetAddress,InetAddress::Hasher> allocatedIps;
+ int64_t mostRecentDeauthTime;
+ std::mutex lock;
+ };
+
+ void _memberChanged(nlohmann::json &old,nlohmann::json &memberConfig,bool push);
+ void _networkChanged(nlohmann::json &old,nlohmann::json &networkConfig,bool push);
+ void _fillSummaryInfo(const std::shared_ptr<_Network> &nw,NetworkSummaryInfo &info);
+
+ EmbeddedNetworkController *const _controller;
+ const Identity _myId;
+ const Address _myAddress;
+ const std::string _path;
+ std::string _myAddressStr;
+
+ std::unordered_map< uint64_t,std::shared_ptr<_Network> > _networks;
+ std::unordered_multimap< uint64_t,uint64_t > _networkByMember;
+ mutable std::mutex _networks_l;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp
index 597bc9c9..9a07b285 100644
--- a/controller/EmbeddedNetworkController.cpp
+++ b/controller/EmbeddedNetworkController.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2015 ZeroTier, Inc.
+ * Copyright (C) 2011-2018 ZeroTier, Inc
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -30,39 +30,36 @@
#include <algorithm>
#include <utility>
#include <stdexcept>
-#include <set>
#include <map>
+#include <thread>
+#include <memory>
#include "../include/ZeroTierOne.h"
-#include "../node/Constants.hpp"
+#include "../version.h"
#include "EmbeddedNetworkController.hpp"
#include "../node/Node.hpp"
-#include "../node/Utils.hpp"
#include "../node/CertificateOfMembership.hpp"
#include "../node/NetworkConfig.hpp"
#include "../node/Dictionary.hpp"
-#include "../node/InetAddress.hpp"
#include "../node/MAC.hpp"
-#include "../node/Address.hpp"
using json = nlohmann::json;
// API version reported via JSON control plane
-#define ZT_NETCONF_CONTROLLER_API_VERSION 3
-
-// Number of requests to remember in member history
-#define ZT_NETCONF_DB_MEMBER_HISTORY_LENGTH 2
+#define ZT_NETCONF_CONTROLLER_API_VERSION 4
// Min duration between requests for an address/nwid combo to prevent floods
#define ZT_NETCONF_MIN_REQUEST_PERIOD 1000
-// Nodes are considered active if they've queried in less than this long
-#define ZT_NETCONF_NODE_ACTIVE_THRESHOLD (ZT_NETWORK_AUTOCONF_DELAY * 2)
+// Global maximum size of arrays in JSON objects
+#define ZT_CONTROLLER_MAX_ARRAY_SIZE 16384
namespace ZeroTier {
+namespace {
+
static json _renderRule(ZT_VirtualNetworkRule &rule)
{
char tmp[128];
@@ -78,19 +75,19 @@ static json _renderRule(ZT_VirtualNetworkRule &rule)
break;
case ZT_NETWORK_RULE_ACTION_TEE:
r["type"] = "ACTION_TEE";
- r["address"] = Address(rule.v.fwd.address).toString();
+ r["address"] = Address(rule.v.fwd.address).toString(tmp);
r["flags"] = (unsigned int)rule.v.fwd.flags;
r["length"] = (unsigned int)rule.v.fwd.length;
break;
case ZT_NETWORK_RULE_ACTION_WATCH:
r["type"] = "ACTION_WATCH";
- r["address"] = Address(rule.v.fwd.address).toString();
+ r["address"] = Address(rule.v.fwd.address).toString(tmp);
r["flags"] = (unsigned int)rule.v.fwd.flags;
r["length"] = (unsigned int)rule.v.fwd.length;
break;
case ZT_NETWORK_RULE_ACTION_REDIRECT:
r["type"] = "ACTION_REDIRECT";
- r["address"] = Address(rule.v.fwd.address).toString();
+ r["address"] = Address(rule.v.fwd.address).toString(tmp);
r["flags"] = (unsigned int)rule.v.fwd.flags;
break;
case ZT_NETWORK_RULE_ACTION_BREAK:
@@ -104,11 +101,11 @@ static json _renderRule(ZT_VirtualNetworkRule &rule)
switch(rt) {
case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
r["type"] = "MATCH_SOURCE_ZEROTIER_ADDRESS";
- r["zt"] = Address(rule.v.zt).toString();
+ r["zt"] = Address(rule.v.zt).toString(tmp);
break;
case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
r["type"] = "MATCH_DEST_ZEROTIER_ADDRESS";
- r["zt"] = Address(rule.v.zt).toString();
+ r["zt"] = Address(rule.v.zt).toString(tmp);
break;
case ZT_NETWORK_RULE_MATCH_VLAN_ID:
r["type"] = "MATCH_VLAN_ID";
@@ -124,29 +121,29 @@ static json _renderRule(ZT_VirtualNetworkRule &rule)
break;
case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
r["type"] = "MATCH_MAC_SOURCE";
- Utils::snprintf(tmp,sizeof(tmp),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(unsigned int)rule.v.mac[0],(unsigned int)rule.v.mac[1],(unsigned int)rule.v.mac[2],(unsigned int)rule.v.mac[3],(unsigned int)rule.v.mac[4],(unsigned int)rule.v.mac[5]);
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(unsigned int)rule.v.mac[0],(unsigned int)rule.v.mac[1],(unsigned int)rule.v.mac[2],(unsigned int)rule.v.mac[3],(unsigned int)rule.v.mac[4],(unsigned int)rule.v.mac[5]);
r["mac"] = tmp;
break;
case ZT_NETWORK_RULE_MATCH_MAC_DEST:
r["type"] = "MATCH_MAC_DEST";
- Utils::snprintf(tmp,sizeof(tmp),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(unsigned int)rule.v.mac[0],(unsigned int)rule.v.mac[1],(unsigned int)rule.v.mac[2],(unsigned int)rule.v.mac[3],(unsigned int)rule.v.mac[4],(unsigned int)rule.v.mac[5]);
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(unsigned int)rule.v.mac[0],(unsigned int)rule.v.mac[1],(unsigned int)rule.v.mac[2],(unsigned int)rule.v.mac[3],(unsigned int)rule.v.mac[4],(unsigned int)rule.v.mac[5]);
r["mac"] = tmp;
break;
case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
r["type"] = "MATCH_IPV4_SOURCE";
- r["ip"] = InetAddress(&(rule.v.ipv4.ip),4,(unsigned int)rule.v.ipv4.mask).toString();
+ r["ip"] = InetAddress(&(rule.v.ipv4.ip),4,(unsigned int)rule.v.ipv4.mask).toString(tmp);
break;
case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
r["type"] = "MATCH_IPV4_DEST";
- r["ip"] = InetAddress(&(rule.v.ipv4.ip),4,(unsigned int)rule.v.ipv4.mask).toString();
+ r["ip"] = InetAddress(&(rule.v.ipv4.ip),4,(unsigned int)rule.v.ipv4.mask).toString(tmp);
break;
case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
r["type"] = "MATCH_IPV6_SOURCE";
- r["ip"] = InetAddress(rule.v.ipv6.ip,16,(unsigned int)rule.v.ipv6.mask).toString();
+ r["ip"] = InetAddress(rule.v.ipv6.ip,16,(unsigned int)rule.v.ipv6.mask).toString(tmp);
break;
case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
r["type"] = "MATCH_IPV6_DEST";
- r["ip"] = InetAddress(rule.v.ipv6.ip,16,(unsigned int)rule.v.ipv6.mask).toString();
+ r["ip"] = InetAddress(rule.v.ipv6.ip,16,(unsigned int)rule.v.ipv6.mask).toString(tmp);
break;
case ZT_NETWORK_RULE_MATCH_IP_TOS:
r["type"] = "MATCH_IP_TOS";
@@ -181,7 +178,7 @@ static json _renderRule(ZT_VirtualNetworkRule &rule)
break;
case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
r["type"] = "MATCH_CHARACTERISTICS";
- Utils::snprintf(tmp,sizeof(tmp),"%.16llx",rule.v.characteristics);
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",rule.v.characteristics);
r["mask"] = tmp;
break;
case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
@@ -228,6 +225,16 @@ static json _renderRule(ZT_VirtualNetworkRule &rule)
r["id"] = rule.v.tag.id;
r["value"] = rule.v.tag.value;
break;
+ case ZT_NETWORK_RULE_MATCH_INTEGER_RANGE:
+ r["type"] = "INTEGER_RANGE";
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",rule.v.intRange.start);
+ r["start"] = tmp;
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",rule.v.intRange.start + (uint64_t)rule.v.intRange.end);
+ r["end"] = tmp;
+ r["idx"] = rule.v.intRange.idx;
+ r["little"] = ((rule.v.intRange.format & 0x80) != 0);
+ r["bits"] = (rule.v.intRange.format & 63) + 1;
+ break;
default:
break;
}
@@ -314,29 +321,29 @@ static bool _parseRule(json &r,ZT_VirtualNetworkRule &rule)
return true;
} else if (t == "MATCH_IPV4_SOURCE") {
rule.t |= ZT_NETWORK_RULE_MATCH_IPV4_SOURCE;
- InetAddress ip(OSUtils::jsonString(r["ip"],"0.0.0.0"));
+ InetAddress ip(OSUtils::jsonString(r["ip"],"0.0.0.0").c_str());
rule.v.ipv4.ip = reinterpret_cast<struct sockaddr_in *>(&ip)->sin_addr.s_addr;
rule.v.ipv4.mask = Utils::ntoh(reinterpret_cast<struct sockaddr_in *>(&ip)->sin_port) & 0xff;
if (rule.v.ipv4.mask > 32) rule.v.ipv4.mask = 32;
return true;
} else if (t == "MATCH_IPV4_DEST") {
rule.t |= ZT_NETWORK_RULE_MATCH_IPV4_DEST;
- InetAddress ip(OSUtils::jsonString(r["ip"],"0.0.0.0"));
+ InetAddress ip(OSUtils::jsonString(r["ip"],"0.0.0.0").c_str());
rule.v.ipv4.ip = reinterpret_cast<struct sockaddr_in *>(&ip)->sin_addr.s_addr;
rule.v.ipv4.mask = Utils::ntoh(reinterpret_cast<struct sockaddr_in *>(&ip)->sin_port) & 0xff;
if (rule.v.ipv4.mask > 32) rule.v.ipv4.mask = 32;
return true;
} else if (t == "MATCH_IPV6_SOURCE") {
rule.t |= ZT_NETWORK_RULE_MATCH_IPV6_SOURCE;
- InetAddress ip(OSUtils::jsonString(r["ip"],"::0"));
- memcpy(rule.v.ipv6.ip,reinterpret_cast<struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
+ InetAddress ip(OSUtils::jsonString(r["ip"],"::0").c_str());
+ ZT_FAST_MEMCPY(rule.v.ipv6.ip,reinterpret_cast<struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
rule.v.ipv6.mask = Utils::ntoh(reinterpret_cast<struct sockaddr_in6 *>(&ip)->sin6_port) & 0xff;
if (rule.v.ipv6.mask > 128) rule.v.ipv6.mask = 128;
return true;
} else if (t == "MATCH_IPV6_DEST") {
rule.t |= ZT_NETWORK_RULE_MATCH_IPV6_DEST;
- InetAddress ip(OSUtils::jsonString(r["ip"],"::0"));
- memcpy(rule.v.ipv6.ip,reinterpret_cast<struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
+ InetAddress ip(OSUtils::jsonString(r["ip"],"::0").c_str());
+ ZT_FAST_MEMCPY(rule.v.ipv6.ip,reinterpret_cast<struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
rule.v.ipv6.mask = Utils::ntoh(reinterpret_cast<struct sockaddr_in6 *>(&ip)->sin6_port) & 0xff;
if (rule.v.ipv6.mask > 128) rule.v.ipv6.mask = 128;
return true;
@@ -418,7 +425,26 @@ static bool _parseRule(json &r,ZT_VirtualNetworkRule &rule)
} else if (t == "MATCH_TAG_RECEIVER") {
rule.t |= ZT_NETWORK_RULE_MATCH_TAG_RECEIVER;
tag = true;
+ } else if (t == "INTEGER_RANGE") {
+ json &s = r["start"];
+ if (s.is_string()) {
+ std::string tmp = s;
+ rule.v.intRange.start = Utils::hexStrToU64(tmp.c_str());
+ } else {
+ rule.v.intRange.start = OSUtils::jsonInt(s,0ULL);
+ }
+ json &e = r["end"];
+ if (e.is_string()) {
+ std::string tmp = e;
+ rule.v.intRange.end = (uint32_t)(Utils::hexStrToU64(tmp.c_str()) - rule.v.intRange.start);
+ } else {
+ rule.v.intRange.end = (uint32_t)(OSUtils::jsonInt(e,0ULL) - rule.v.intRange.start);
+ }
+ rule.v.intRange.idx = (uint16_t)OSUtils::jsonInt(r["idx"],0ULL);
+ rule.v.intRange.format = (OSUtils::jsonBool(r["little"],false)) ? 0x80 : 0x00;
+ rule.v.intRange.format |= (uint8_t)((OSUtils::jsonInt(r["bits"],1ULL) - 1) & 63);
}
+
if (tag) {
rule.v.tag.id = (uint32_t)(OSUtils::jsonInt(r["id"],0ULL) & 0xffffffffULL);
rule.v.tag.value = (uint32_t)(OSUtils::jsonInt(r["value"],0ULL) & 0xffffffffULL);
@@ -428,29 +454,37 @@ static bool _parseRule(json &r,ZT_VirtualNetworkRule &rule)
return false;
}
+} // anonymous namespace
+
EmbeddedNetworkController::EmbeddedNetworkController(Node *node,const char *dbPath) :
_startTime(OSUtils::now()),
- _threadsStarted(false),
- _db(dbPath),
- _node(node)
+ _node(node),
+ _path(dbPath),
+ _sender((NetworkController::Sender *)0)
{
}
EmbeddedNetworkController::~EmbeddedNetworkController()
{
- Mutex::Lock _l(_threads_m);
- if (_threadsStarted) {
- for(int i=0;i<(ZT_EMBEDDEDNETWORKCONTROLLER_BACKGROUND_THREAD_COUNT*2);++i)
- _queue.post((_RQEntry *)0);
- for(int i=0;i<ZT_EMBEDDEDNETWORKCONTROLLER_BACKGROUND_THREAD_COUNT;++i)
- Thread::join(_threads[i]);
- }
+ std::lock_guard<std::mutex> l(_threads_l);
+ _queue.stop();
+ for(auto t=_threads.begin();t!=_threads.end();++t)
+ t->join();
}
void EmbeddedNetworkController::init(const Identity &signingId,Sender *sender)
{
- this->_sender = sender;
- this->_signingId = signingId;
+ char tmp[64];
+ _signingId = signingId;
+ _sender = sender;
+ _signingIdAddressString = signingId.address().toString(tmp);
+#ifdef ZT_CONTROLLER_USE_RETHINKDB
+ if ((_path.length() > 10)&&(_path.substr(0,10) == "rethinkdb:"))
+ _db.reset(new RethinkDB(this,_signingId,_path.c_str()));
+ else // else use FileDB after endif
+#endif
+ _db.reset(new FileDB(this,_signingId,_path.c_str()));
+ _db->waitForReady();
}
void EmbeddedNetworkController::request(
@@ -462,22 +496,14 @@ void EmbeddedNetworkController::request(
{
if (((!_signingId)||(!_signingId.hasPrivate()))||(_signingId.address().toInt() != (nwid >> 24))||(!_sender))
return;
-
- {
- Mutex::Lock _l(_threads_m);
- if (!_threadsStarted) {
- for(int i=0;i<ZT_EMBEDDEDNETWORKCONTROLLER_BACKGROUND_THREAD_COUNT;++i)
- _threads[i] = Thread::start(this);
- }
- _threadsStarted = true;
- }
-
+ _startThreads();
_RQEntry *qe = new _RQEntry;
qe->nwid = nwid;
qe->requestPacketId = requestPacketId;
qe->fromAddr = fromAddr;
qe->identity = identity;
qe->metaData = metaData;
+ qe->type = _RQEntry::RQENTRY_TYPE_REQUEST;
_queue.post(qe);
}
@@ -489,19 +515,15 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpGET(
std::string &responseBody,
std::string &responseContentType)
{
+ if (!_db)
+ return 500;
+
if ((path.size() > 0)&&(path[0] == "network")) {
if ((path.size() >= 2)&&(path[1].length() == 16)) {
const uint64_t nwid = Utils::hexStrToU64(path[1].c_str());
- char nwids[24];
- Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid);
-
json network;
- {
- Mutex::Lock _l(_db_m);
- network = _db.get("network",nwids);
- }
- if (!network.size())
+ if (!_db->get(nwid,network))
return 404;
if (path.size() >= 3) {
@@ -509,82 +531,72 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpGET(
if (path[2] == "member") {
if (path.size() >= 4) {
- const uint64_t address = Utils::hexStrToU64(path[3].c_str());
+ // Get member
+ const uint64_t address = Utils::hexStrToU64(path[3].c_str());
json member;
- {
- Mutex::Lock _l(_db_m);
- member = _db.get("network",nwids,"member",Address(address).toString());
- }
- if (!member.size())
+ if (!_db->get(nwid,network,address,member))
return 404;
-
- _addMemberNonPersistedFields(member,OSUtils::now());
responseBody = OSUtils::jsonDump(member);
responseContentType = "application/json";
- return 200;
} else {
-
- Mutex::Lock _l(_db_m);
+ // List members and their revisions
responseBody = "{";
- _db.filter((std::string("network/") + nwids + "/member/"),[&responseBody](const std::string &n,const json &member) {
- if ((member.is_object())&&(member.size() > 0)) {
- responseBody.append((responseBody.length() == 1) ? "\"" : ",\"");
- responseBody.append(OSUtils::jsonString(member["id"],"0"));
- responseBody.append("\":");
- responseBody.append(OSUtils::jsonString(member["revision"],"0"));
+ std::vector<json> members;
+ if (_db->get(nwid,network,members)) {
+ responseBody.reserve((members.size() + 2) * 32);
+ std::string mid;
+ for(auto member=members.begin();member!=members.end();++member) {
+ mid = (*member)["id"];
+ char tmp[128];
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%s\"%s\":%llu",(responseBody.length() > 1) ? ",\"" : "\"",mid.c_str(),(unsigned long long)OSUtils::jsonInt((*member)["revision"],0));
+ responseBody.append(tmp);
}
- return true; // never delete
- });
+ }
responseBody.push_back('}');
responseContentType = "application/json";
- return 200;
}
+ return 200;
} // else 404
} else {
+ // Get network
- const uint64_t now = OSUtils::now();
- _NetworkMemberInfo nmi;
- _getNetworkMemberInfo(now,nwid,nmi);
- _addNetworkNonPersistedFields(network,now,nmi);
responseBody = OSUtils::jsonDump(network);
responseContentType = "application/json";
return 200;
}
} else if (path.size() == 1) {
-
- std::set<std::string> networkIds;
- {
- Mutex::Lock _l(_db_m);
- _db.filter("network/",[&networkIds](const std::string &n,const json &obj) {
- if (n.length() == (16 + 8))
- networkIds.insert(n.substr(8));
- return true; // do not delete
- });
- }
-
- responseBody.push_back('[');
- for(std::set<std::string>::iterator i(networkIds.begin());i!=networkIds.end();++i) {
- responseBody.append((responseBody.length() == 1) ? "\"" : ",\"");
- responseBody.append(*i);
- responseBody.append("\"");
+ // List networks
+
+ std::vector<uint64_t> networkIds;
+ _db->networks(networkIds);
+ char tmp[64];
+ responseBody = "[";
+ responseBody.reserve((networkIds.size() + 1) * 24);
+ for(std::vector<uint64_t>::const_iterator i(networkIds.begin());i!=networkIds.end();++i) {
+ if (responseBody.length() > 1)
+ responseBody.push_back(',');
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"\"%.16llx\"",(unsigned long long)*i);
+ responseBody.append(tmp);
}
responseBody.push_back(']');
responseContentType = "application/json";
+
return 200;
} // else 404
} else {
+ // Controller status
char tmp[4096];
- Utils::snprintf(tmp,sizeof(tmp),"{\n\t\"controller\": true,\n\t\"apiVersion\": %d,\n\t\"clock\": %llu\n}\n",ZT_NETCONF_CONTROLLER_API_VERSION,(unsigned long long)OSUtils::now());
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"{\n\t\"controller\": true,\n\t\"apiVersion\": %d,\n\t\"clock\": %llu\n}\n",ZT_NETCONF_CONTROLLER_API_VERSION,(unsigned long long)OSUtils::now());
responseBody = tmp;
responseContentType = "application/json";
return 200;
@@ -602,6 +614,8 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
std::string &responseBody,
std::string &responseContentType)
{
+ if (!_db)
+ return 500;
if (path.empty())
return 404;
@@ -618,58 +632,49 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
responseContentType = "application/json";
return 400;
}
- const uint64_t now = OSUtils::now();
+ const int64_t now = OSUtils::now();
if (path[0] == "network") {
if ((path.size() >= 2)&&(path[1].length() == 16)) {
uint64_t nwid = Utils::hexStrToU64(path[1].c_str());
char nwids[24];
- Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid);
+ OSUtils::ztsnprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid);
if (path.size() >= 3) {
if ((path.size() == 4)&&(path[2] == "member")&&(path[3].length() == 10)) {
uint64_t address = Utils::hexStrToU64(path[3].c_str());
char addrs[24];
- Utils::snprintf(addrs,sizeof(addrs),"%.10llx",(unsigned long long)address);
+ OSUtils::ztsnprintf(addrs,sizeof(addrs),"%.10llx",(unsigned long long)address);
- json member;
- {
- Mutex::Lock _l(_db_m);
- member = _db.get("network",nwids,"member",Address(address).toString());
- }
+ json member,network;
+ _db->get(nwid,network,address,member);
json origMember(member); // for detecting changes
- _initMember(member);
+ DB::initMember(member);
try {
if (b.count("activeBridge")) member["activeBridge"] = OSUtils::jsonBool(b["activeBridge"],false);
if (b.count("noAutoAssignIps")) member["noAutoAssignIps"] = OSUtils::jsonBool(b["noAutoAssignIps"],false);
+ if (b.count("remoteTraceTarget")) {
+ const std::string rtt(OSUtils::jsonString(b["remoteTraceTarget"],""));
+ if (rtt.length() == 10) {
+ member["remoteTraceTarget"] = rtt;
+ } else {
+ member["remoteTraceTarget"] = json();
+ }
+ }
+ if (b.count("remoteTraceLevel")) member["remoteTraceLevel"] = OSUtils::jsonInt(b["remoteTraceLevel"],0ULL);
+
if (b.count("authorized")) {
const bool newAuth = OSUtils::jsonBool(b["authorized"],false);
if (newAuth != OSUtils::jsonBool(member["authorized"],false)) {
member["authorized"] = newAuth;
member[((newAuth) ? "lastAuthorizedTime" : "lastDeauthorizedTime")] = now;
-
- json ah;
- ah["a"] = newAuth;
- ah["by"] = "api";
- ah["ts"] = now;
- ah["ct"] = json();
- ah["c"] = json();
- member["authHistory"].push_back(ah);
-
- // Member is being de-authorized, so spray Revocation objects to all online members
- if (!newAuth) {
- _clearNetworkMemberInfoCache(nwid);
- Revocation rev((uint32_t)_node->prng(),nwid,0,now,ZT_REVOCATION_FLAG_FAST_PROPAGATE,Address(address),Revocation::CREDENTIAL_TYPE_COM);
- rev.sign(_signingId);
- Mutex::Lock _l(_lastRequestTime_m);
- for(std::map< std::pair<uint64_t,uint64_t>,uint64_t >::iterator i(_lastRequestTime.begin());i!=_lastRequestTime.end();++i) {
- if ((now - i->second) < ZT_NETWORK_AUTOCONF_DELAY)
- _node->ncSendRevocation(Address(i->first.first),rev);
- }
+ if (newAuth) {
+ member["lastAuthorizedCredentialType"] = "api";
+ member["lastAuthorizedCredential"] = json();
}
}
}
@@ -680,9 +685,12 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
json mipa(json::array());
for(unsigned long i=0;i<ipa.size();++i) {
std::string ips = ipa[i];
- InetAddress ip(ips);
+ InetAddress ip(ips.c_str());
if ((ip.ss_family == AF_INET)||(ip.ss_family == AF_INET6)) {
- mipa.push_back(ip.toIpString());
+ char tmpip[64];
+ mipa.push_back(ip.toIpString(tmpip));
+ if (mipa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+ break;
}
}
member["ipAssignments"] = mipa;
@@ -704,6 +712,8 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
ta.push_back(t->first);
ta.push_back(t->second);
mtagsa.push_back(ta);
+ if (mtagsa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+ break;
}
member["tags"] = mtagsa;
}
@@ -715,6 +725,8 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
json mcaps = json::array();
for(unsigned long i=0;i<capabilities.size();++i) {
mcaps.push_back(OSUtils::jsonInt(capabilities[i],0ULL));
+ if (mcaps.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+ break;
}
std::sort(mcaps.begin(),mcaps.end());
mcaps.erase(std::unique(mcaps.begin(),mcaps.end()),mcaps.end());
@@ -731,115 +743,57 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
member["address"] = addrs; // legacy
member["nwid"] = nwids;
- if (member != origMember) {
- member["lastModified"] = now;
- json &revj = member["revision"];
- member["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL);
- {
- Mutex::Lock _l(_db_m);
- _db.put("network",nwids,"member",Address(address).toString(),member);
- }
- _pushMemberUpdate(now,nwid,member);
- }
-
- // Add non-persisted fields
- member["clock"] = now;
-
+ DB::cleanMember(member);
+ _db->save(&origMember,member);
responseBody = OSUtils::jsonDump(member);
responseContentType = "application/json";
- return 200;
- } else if ((path.size() == 3)&&(path[2] == "test")) {
-
- Mutex::Lock _l(_tests_m);
-
- _tests.push_back(ZT_CircuitTest());
- ZT_CircuitTest *const test = &(_tests.back());
- memset(test,0,sizeof(ZT_CircuitTest));
-
- Utils::getSecureRandom(&(test->testId),sizeof(test->testId));
- test->credentialNetworkId = nwid;
- test->ptr = (void *)this;
- json hops = b["hops"];
- if (hops.is_array()) {
- for(unsigned long i=0;i<hops.size();++i) {
- json &hops2 = hops[i];
- if (hops2.is_array()) {
- for(unsigned long j=0;j<hops2.size();++j) {
- std::string s = hops2[j];
- test->hops[test->hopCount].addresses[test->hops[test->hopCount].breadth++] = Utils::hexStrToU64(s.c_str()) & 0xffffffffffULL;
- }
- ++test->hopCount;
- } else if (hops2.is_string()) {
- std::string s = hops2;
- test->hops[test->hopCount].addresses[test->hops[test->hopCount].breadth++] = Utils::hexStrToU64(s.c_str()) & 0xffffffffffULL;
- ++test->hopCount;
- }
- }
- }
- test->reportAtEveryHop = (OSUtils::jsonBool(b["reportAtEveryHop"],true) ? 1 : 0);
-
- if (!test->hopCount) {
- _tests.pop_back();
- responseBody = "{ \"message\": \"a test must contain at least one hop\" }";
- responseContentType = "application/json";
- return 400;
- }
-
- test->timestamp = OSUtils::now();
-
- if (_node) {
- _node->circuitTestBegin((void *)0,test,&(EmbeddedNetworkController::_circuitTestCallback));
- } else {
- _tests.pop_back();
- return 500;
- }
-
- char json[512];
- Utils::snprintf(json,sizeof(json),"{\"testId\":\"%.16llx\",\"timestamp\":%llu}",test->testId,test->timestamp);
- responseBody = json;
- responseContentType = "application/json";
return 200;
-
} // else 404
} else {
// POST to network ID
- json network;
- {
- Mutex::Lock _l(_db_m);
-
- // Magic ID ending with ______ picks a random unused network ID
- if (path[1].substr(10) == "______") {
- nwid = 0;
- uint64_t nwidPrefix = (Utils::hexStrToU64(path[1].substr(0,10).c_str()) << 24) & 0xffffffffff000000ULL;
- uint64_t nwidPostfix = 0;
- for(unsigned long k=0;k<100000;++k) { // sanity limit on trials
- Utils::getSecureRandom(&nwidPostfix,sizeof(nwidPostfix));
- uint64_t tryNwid = nwidPrefix | (nwidPostfix & 0xffffffULL);
- if ((tryNwid & 0xffffffULL) == 0ULL) tryNwid |= 1ULL;
- Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)tryNwid);
- if (_db.get("network",nwids).size() <= 0) {
- nwid = tryNwid;
- break;
- }
+ // Magic ID ending with ______ picks a random unused network ID
+ if (path[1].substr(10) == "______") {
+ nwid = 0;
+ uint64_t nwidPrefix = (Utils::hexStrToU64(path[1].substr(0,10).c_str()) << 24) & 0xffffffffff000000ULL;
+ uint64_t nwidPostfix = 0;
+ for(unsigned long k=0;k<100000;++k) { // sanity limit on trials
+ Utils::getSecureRandom(&nwidPostfix,sizeof(nwidPostfix));
+ uint64_t tryNwid = nwidPrefix | (nwidPostfix & 0xffffffULL);
+ if ((tryNwid & 0xffffffULL) == 0ULL) tryNwid |= 1ULL;
+ if (!_db->hasNetwork(tryNwid)) {
+ nwid = tryNwid;
+ break;
}
- if (!nwid)
- return 503;
}
-
- network = _db.get("network",nwids);
+ if (!nwid)
+ return 503;
}
+ OSUtils::ztsnprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid);
+
+ json network;
+ _db->get(nwid,network);
json origNetwork(network); // for detecting changes
- _initNetwork(network);
+ DB::initNetwork(network);
try {
if (b.count("name")) network["name"] = OSUtils::jsonString(b["name"],"");
if (b.count("private")) network["private"] = OSUtils::jsonBool(b["private"],true);
if (b.count("enableBroadcast")) network["enableBroadcast"] = OSUtils::jsonBool(b["enableBroadcast"],false);
- if (b.count("allowPassiveBridging")) network["allowPassiveBridging"] = OSUtils::jsonBool(b["allowPassiveBridging"],false);
if (b.count("multicastLimit")) network["multicastLimit"] = OSUtils::jsonInt(b["multicastLimit"],32ULL);
+ if (b.count("mtu")) network["mtu"] = std::max(std::min((unsigned int)OSUtils::jsonInt(b["mtu"],ZT_DEFAULT_MTU),(unsigned int)ZT_MAX_MTU),(unsigned int)ZT_MIN_MTU);
+
+ if (b.count("remoteTraceTarget")) {
+ const std::string rtt(OSUtils::jsonString(b["remoteTraceTarget"],""));
+ if (rtt.length() == 10) {
+ network["remoteTraceTarget"] = rtt;
+ } else {
+ network["remoteTraceTarget"] = json();
+ }
+ }
+ if (b.count("remoteTraceLevel")) network["remoteTraceLevel"] = OSUtils::jsonInt(b["remoteTraceLevel"],0ULL);
if (b.count("v4AssignMode")) {
json nv4m;
@@ -893,16 +847,19 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
json &target = rt["target"];
json &via = rt["via"];
if (target.is_string()) {
- InetAddress t(target.get<std::string>());
+ InetAddress t(target.get<std::string>().c_str());
InetAddress v;
- if (via.is_string()) v.fromString(via.get<std::string>());
+ if (via.is_string()) v.fromString(via.get<std::string>().c_str());
if ( ((t.ss_family == AF_INET)||(t.ss_family == AF_INET6)) && (t.netmaskBitsValid()) ) {
json tmp;
- tmp["target"] = t.toString();
+ char tmp2[64];
+ tmp["target"] = t.toString(tmp2);
if (v.ss_family == t.ss_family)
- tmp["via"] = v.toIpString();
+ tmp["via"] = v.toIpString(tmp2);
else tmp["via"] = json();
nrts.push_back(tmp);
+ if (nrts.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+ break;
}
}
}
@@ -918,13 +875,16 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
for(unsigned long i=0;i<ipp.size();++i) {
json &ip = ipp[i];
if ((ip.is_object())&&(ip.count("ipRangeStart"))&&(ip.count("ipRangeEnd"))) {
- InetAddress f(OSUtils::jsonString(ip["ipRangeStart"],""));
- InetAddress t(OSUtils::jsonString(ip["ipRangeEnd"],""));
+ InetAddress f(OSUtils::jsonString(ip["ipRangeStart"],"").c_str());
+ InetAddress t(OSUtils::jsonString(ip["ipRangeEnd"],"").c_str());
if ( ((f.ss_family == AF_INET)||(f.ss_family == AF_INET6)) && (f.ss_family == t.ss_family) ) {
json tmp = json::object();
- tmp["ipRangeStart"] = f.toIpString();
- tmp["ipRangeEnd"] = t.toIpString();
+ char tmp2[64];
+ tmp["ipRangeStart"] = f.toIpString(tmp2);
+ tmp["ipRangeEnd"] = t.toIpString(tmp2);
nipp.push_back(tmp);
+ if (nipp.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+ break;
}
}
}
@@ -940,8 +900,11 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
json &rule = rules[i];
if (rule.is_object()) {
ZT_VirtualNetworkRule ztr;
- if (_parseRule(rule,ztr))
+ if (_parseRule(rule,ztr)) {
nrules.push_back(_renderRule(ztr));
+ if (nrules.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+ break;
+ }
}
}
network["rules"] = nrules;
@@ -950,22 +913,15 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
if (b.count("authTokens")) {
json &authTokens = b["authTokens"];
- if (authTokens.is_array()) {
- json nat = json::array();
- for(unsigned long i=0;i<authTokens.size();++i) {
- json &token = authTokens[i];
- if (token.is_object()) {
- std::string tstr = token["token"];
- if (tstr.length() > 0) {
- json t = json::object();
- t["token"] = tstr;
- t["expires"] = OSUtils::jsonInt(token["expires"],0ULL);
- t["maxUsesPerMember"] = OSUtils::jsonInt(token["maxUsesPerMember"],0ULL);
- nat.push_back(t);
- }
- }
+ if (authTokens.is_object()) {
+ json nat;
+ for(json::iterator t(authTokens.begin());t!=authTokens.end();++t) {
+ if ((t.value().is_number())&&(t.value() >= 0))
+ nat[t.key()] = t.value();
}
network["authTokens"] = nat;
+ } else {
+ network["authTokens"] = {{}};
}
}
@@ -988,8 +944,11 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
json &rule = rules[i];
if (rule.is_object()) {
ZT_VirtualNetworkRule ztr;
- if (_parseRule(rule,ztr))
+ if (_parseRule(rule,ztr)) {
nrules.push_back(_renderRule(ztr));
+ if (nrules.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+ break;
+ }
}
}
}
@@ -1000,8 +959,11 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
}
json ncapsa = json::array();
- for(std::map< uint64_t,json >::iterator c(ncaps.begin());c!=ncaps.end();++c)
+ for(std::map< uint64_t,json >::iterator c(ncaps.begin());c!=ncaps.end();++c) {
ncapsa.push_back(c->second);
+ if (ncapsa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+ break;
+ }
network["capabilities"] = ncapsa;
}
}
@@ -1025,8 +987,11 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
}
json ntagsa = json::array();
- for(std::map< uint64_t,json >::iterator t(ntags.begin());t!=ntags.end();++t)
+ for(std::map< uint64_t,json >::iterator t(ntags.begin());t!=ntags.end();++t) {
ntagsa.push_back(t->second);
+ if (ntagsa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+ break;
+ }
network["tags"] = ntagsa;
}
}
@@ -1040,25 +1005,8 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
network["id"] = nwids;
network["nwid"] = nwids; // legacy
- if (network != origNetwork) {
- json &revj = network["revision"];
- network["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL);
- network["lastModified"] = now;
- {
- Mutex::Lock _l(_db_m);
- _db.put("network",nwids,network);
- }
-
- // Send an update to all members of the network
- _db.filter((std::string("network/") + nwids + "/member/"),[this,&now,&nwid](const std::string &n,const json &obj) {
- _pushMemberUpdate(now,nwid,obj);
- return true; // do not delete
- });
- }
-
- _NetworkMemberInfo nmi;
- _getNetworkMemberInfo(now,nwid,nmi);
- _addNetworkNonPersistedFields(network,now,nmi);
+ DB::cleanNetwork(network);
+ _db->save(&origNetwork,network);
responseBody = OSUtils::jsonDump(network);
responseContentType = "application/json";
@@ -1067,18 +1015,6 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
} // else 404
- } else if (path[0] == "ping") {
-
- json testRec;
- const uint64_t now = OSUtils::now();
- testRec["clock"] = now;
- testRec["uptime"] = (now - _startTime);
- testRec["content"] = b;
- responseBody = OSUtils::jsonDump(testRec);
- _db.writeRaw("pong",responseBody);
- responseContentType = "application/json";
- return 200;
-
}
return 404;
@@ -1092,31 +1028,25 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpDELETE(
std::string &responseBody,
std::string &responseContentType)
{
+ if (!_db)
+ return 500;
if (path.empty())
return 404;
if (path[0] == "network") {
if ((path.size() >= 2)&&(path[1].length() == 16)) {
const uint64_t nwid = Utils::hexStrToU64(path[1].c_str());
-
- char nwids[24];
- Utils::snprintf(nwids,sizeof(nwids),"%.16llx",nwid);
- json network;
- {
- Mutex::Lock _l(_db_m);
- network = _db.get("network",nwids);
- }
- if (!network.size())
- return 404;
-
if (path.size() >= 3) {
if ((path.size() == 4)&&(path[2] == "member")&&(path[3].length() == 10)) {
const uint64_t address = Utils::hexStrToU64(path[3].c_str());
- Mutex::Lock _l(_db_m);
+ json network,member;
+ _db->get(nwid,network,address,member);
- json member = _db.get("network",nwids,"member",Address(address).toString());
- _db.erase("network",nwids,"member",Address(address).toString());
+ {
+ std::lock_guard<std::mutex> l(_memberStatus_l);
+ _memberStatus.erase(_MemberStatusKey(nwid,address));
+ }
if (!member.size())
return 404;
@@ -1125,17 +1055,21 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpDELETE(
return 200;
}
} else {
- Mutex::Lock _l(_db_m);
-
- std::string pfx("network/");
- pfx.append(nwids);
- _db.filter(pfx,[](const std::string &n,const json &obj) {
- return false; // delete
- });
+ json network;
+ _db->get(nwid,network);
+ _db->eraseNetwork(nwid);
- Mutex::Lock _l2(_nmiCache_m);
- _nmiCache.erase(nwid);
+ {
+ std::lock_guard<std::mutex> l(_memberStatus_l);
+ for(auto i=_memberStatus.begin();i!=_memberStatus.end();) {
+ if (i->first.networkId == nwid)
+ _memberStatus.erase(i++);
+ else ++i;
+ }
+ }
+ if (!network.size())
+ return 404;
responseBody = OSUtils::jsonDump(network);
responseContentType = "application/json";
return 200;
@@ -1147,87 +1081,93 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpDELETE(
return 404;
}
-void EmbeddedNetworkController::threadMain()
- throw()
+void EmbeddedNetworkController::handleRemoteTrace(const ZT_RemoteTrace &rt)
{
- uint64_t lastCircuitTestCheck = 0;
- for(;;) {
- _RQEntry *const qe = _queue.get(); // waits on next request
- if (!qe) break; // enqueue a NULL to terminate threads
- try {
- _request(qe->nwid,qe->fromAddr,qe->requestPacketId,qe->identity,qe->metaData);
- } catch ( ... ) {}
- delete qe;
-
- uint64_t now = OSUtils::now();
- if ((now - lastCircuitTestCheck) > ZT_EMBEDDEDNETWORKCONTROLLER_CIRCUIT_TEST_EXPIRATION) {
- lastCircuitTestCheck = now;
- Mutex::Lock _l(_tests_m);
- for(std::list< ZT_CircuitTest >::iterator i(_tests.begin());i!=_tests.end();) {
- if ((now - i->timestamp) > ZT_EMBEDDEDNETWORKCONTROLLER_CIRCUIT_TEST_EXPIRATION) {
- _node->circuitTestEnd(&(*i));
- _tests.erase(i++);
- } else ++i;
+ static volatile unsigned long idCounter = 0;
+ char id[128],tmp[128];
+ std::string k,v;
+
+ if (!_db)
+ return;
+
+ try {
+ // Convert Dictionary into JSON object
+ json d;
+ char *saveptr = (char *)0;
+ for(char *l=Utils::stok(rt.data,"\n",&saveptr);(l);l=Utils::stok((char *)0,"\n",&saveptr)) {
+ char *eq = strchr(l,'=');
+ if (eq > l) {
+ k.assign(l,(unsigned long)(eq - l));
+ v.clear();
+ ++eq;
+ while (*eq) {
+ if (*eq == '\\') {
+ ++eq;
+ if (*eq) {
+ switch(*eq) {
+ case 'r': v.push_back('\r'); break;
+ case 'n': v.push_back('\n'); break;
+ case '0': v.push_back((char)0); break;
+ case 'e': v.push_back('='); break;
+ default: v.push_back(*eq); break;
+ }
+ ++eq;
+ }
+ } else {
+ v.push_back(*(eq++));
+ }
+ }
+ if ((k.length() > 0)&&(v.length() > 0))
+ d[k] = v;
}
}
+
+ const int64_t now = OSUtils::now();
+ OSUtils::ztsnprintf(id,sizeof(id),"%.10llx-%.16llx-%.10llx-%.4x",_signingId.address().toInt(),now,rt.origin,(unsigned int)(idCounter++ & 0xffff));
+ d["id"] = id;
+ d["objtype"] = "trace";
+ d["ts"] = now;
+ d["nodeId"] = Utils::hex10(rt.origin,tmp);
+ _db->save((nlohmann::json *)0,d);
+ } catch ( ... ) {
+ // drop invalid trace messages if an error occurs
+ }
+}
+
+void EmbeddedNetworkController::onNetworkUpdate(const uint64_t networkId)
+{
+ // Send an update to all members of the network that are online
+ const int64_t now = OSUtils::now();
+ std::lock_guard<std::mutex> l(_memberStatus_l);
+ for(auto i=_memberStatus.begin();i!=_memberStatus.end();++i) {
+ if ((i->first.networkId == networkId)&&(i->second.online(now))&&(i->second.lastRequestMetaData))
+ request(networkId,InetAddress(),0,i->second.identity,i->second.lastRequestMetaData);
}
}
-void EmbeddedNetworkController::_circuitTestCallback(ZT_Node *node,ZT_CircuitTest *test,const ZT_CircuitTestReport *report)
+void EmbeddedNetworkController::onNetworkMemberUpdate(const uint64_t networkId,const uint64_t memberId)
{
- char tmp[1024],id[128];
- EmbeddedNetworkController *const self = reinterpret_cast<EmbeddedNetworkController *>(test->ptr);
-
- if ((!test)||(!report)||(!test->credentialNetworkId)) return; // sanity check
-
- const uint64_t now = OSUtils::now();
- Utils::snprintf(id,sizeof(id),"network/%.16llx/test/%.16llx-%.16llx-%.10llx-%.10llx",test->credentialNetworkId,test->testId,now,report->upstream,report->current);
- Utils::snprintf(tmp,sizeof(tmp),
- "{\"id\": \"%s\","
- "\"timestamp\": %llu,"
- "\"networkId\": \"%.16llx\","
- "\"testId\": \"%.16llx\","
- "\"upstream\": \"%.10llx\","
- "\"current\": \"%.10llx\","
- "\"receivedTimestamp\": %llu,"
- "\"sourcePacketId\": \"%.16llx\","
- "\"flags\": %llu,"
- "\"sourcePacketHopCount\": %u,"
- "\"errorCode\": %u,"
- "\"vendor\": %d,"
- "\"protocolVersion\": %u,"
- "\"majorVersion\": %u,"
- "\"minorVersion\": %u,"
- "\"revision\": %u,"
- "\"platform\": %d,"
- "\"architecture\": %d,"
- "\"receivedOnLocalAddress\": \"%s\","
- "\"receivedFromRemoteAddress\": \"%s\","
- "\"receivedFromLinkQuality\": %f}",
- id + 30, // last bit only, not leading path
- (unsigned long long)test->timestamp,
- (unsigned long long)test->credentialNetworkId,
- (unsigned long long)test->testId,
- (unsigned long long)report->upstream,
- (unsigned long long)report->current,
- (unsigned long long)now,
- (unsigned long long)report->sourcePacketId,
- (unsigned long long)report->flags,
- report->sourcePacketHopCount,
- report->errorCode,
- (int)report->vendor,
- report->protocolVersion,
- report->majorVersion,
- report->minorVersion,
- report->revision,
- (int)report->platform,
- (int)report->architecture,
- reinterpret_cast<const InetAddress *>(&(report->receivedOnLocalAddress))->toString().c_str(),
- reinterpret_cast<const InetAddress *>(&(report->receivedFromRemoteAddress))->toString().c_str(),
- ((double)report->receivedFromLinkQuality / (double)ZT_PATH_LINK_QUALITY_MAX));
-
- Mutex::Lock _l(self->_db_m);
- self->_db.writeRaw(id,std::string(tmp));
+ // Push update to member if online
+ try {
+ std::lock_guard<std::mutex> l(_memberStatus_l);
+ _MemberStatus &ms = _memberStatus[_MemberStatusKey(networkId,memberId)];
+ if ((ms.online(OSUtils::now()))&&(ms.lastRequestMetaData))
+ request(networkId,InetAddress(),0,ms.identity,ms.lastRequestMetaData);
+ } catch ( ... ) {}
+}
+
+void EmbeddedNetworkController::onNetworkMemberDeauthorize(const uint64_t networkId,const uint64_t memberId)
+{
+ const int64_t now = OSUtils::now();
+ Revocation rev((uint32_t)_node->prng(),networkId,0,now,ZT_REVOCATION_FLAG_FAST_PROPAGATE,Address(memberId),Revocation::CREDENTIAL_TYPE_COM);
+ rev.sign(_signingId);
+ {
+ std::lock_guard<std::mutex> l(_memberStatus_l);
+ for(auto i=_memberStatus.begin();i!=_memberStatus.end();++i) {
+ if ((i->first.networkId == networkId)&&(i->second.online(now)))
+ _node->ncSendRevocation(Address(i->first.nodeId),rev);
+ }
+ }
}
void EmbeddedNetworkController::_request(
@@ -1237,41 +1177,40 @@ void EmbeddedNetworkController::_request(
const Identity &identity,
const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> &metaData)
{
+ char nwids[24];
+ DB::NetworkSummaryInfo ns;
+ json network,member,origMember;
+
+ if (!_db)
+ return;
+
if (((!_signingId)||(!_signingId.hasPrivate()))||(_signingId.address().toInt() != (nwid >> 24))||(!_sender))
return;
- const uint64_t now = OSUtils::now();
+ const int64_t now = OSUtils::now();
if (requestPacketId) {
- Mutex::Lock _l(_lastRequestTime_m);
- uint64_t &lrt = _lastRequestTime[std::pair<uint64_t,uint64_t>(identity.address().toInt(),nwid)];
- if ((now - lrt) <= ZT_NETCONF_MIN_REQUEST_PERIOD)
+ std::lock_guard<std::mutex> l(_memberStatus_l);
+ _MemberStatus &ms = _memberStatus[_MemberStatusKey(nwid,identity.address().toInt())];
+ if ((now - ms.lastRequestTime) <= ZT_NETCONF_MIN_REQUEST_PERIOD)
return;
- lrt = now;
+ ms.lastRequestTime = now;
}
- char nwids[24];
- Utils::snprintf(nwids,sizeof(nwids),"%.16llx",nwid);
- json network;
- json member;
- {
- Mutex::Lock _l(_db_m);
- network = _db.get("network",nwids);
- member = _db.get("network",nwids,"member",identity.address().toString());
- }
+ _db->nodeIsOnline(nwid,identity.address().toInt(),fromAddr);
- if (!network.size()) {
+ Utils::hex(nwid,nwids);
+ _db->get(nwid,network,identity.address().toInt(),member,ns);
+ if ((!network.is_object())||(network.size() == 0)) {
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_OBJECT_NOT_FOUND);
return;
}
-
- const bool newMember = (member.size() == 0);
-
- json origMember(member); // for detecting modification later
- _initMember(member);
+ origMember = member;
+ const bool newMember = ((!member.is_object())||(member.size() == 0));
+ DB::initMember(member);
{
- std::string haveIdStr(OSUtils::jsonString(member["identity"],""));
+ const std::string haveIdStr(OSUtils::jsonString(member["identity"],""));
if (haveIdStr.length() > 0) {
// If we already know this member's identity perform a full compare. This prevents
// a "collision" from being able to auth onto our network in place of an already
@@ -1287,66 +1226,44 @@ void EmbeddedNetworkController::_request(
}
} else {
// If we do not yet know this member's identity, learn it.
- member["identity"] = identity.toString(false);
+ char idtmp[1024];
+ member["identity"] = identity.toString(false,idtmp);
}
}
// These are always the same, but make sure they are set
- member["id"] = identity.address().toString();
- member["address"] = member["id"];
- member["nwid"] = nwids;
+ {
+ char tmpid[128];
+ const std::string addrs(identity.address().toString(tmpid));
+ member["id"] = addrs;
+ member["address"] = addrs;
+ member["nwid"] = nwids;
+ }
// Determine whether and how member is authorized
- const char *authorizedBy = (const char *)0;
+ bool authorized = false;
bool autoAuthorized = false;
json autoAuthCredentialType,autoAuthCredential;
if (OSUtils::jsonBool(member["authorized"],false)) {
- authorizedBy = "memberIsAuthorized";
+ authorized = true;
} else if (!OSUtils::jsonBool(network["private"],true)) {
- authorizedBy = "networkIsPublic";
- json &ahist = member["authHistory"];
- if ((!ahist.is_array())||(ahist.size() == 0))
- autoAuthorized = true;
+ authorized = true;
+ autoAuthorized = true;
+ autoAuthCredentialType = "public";
} else {
char presentedAuth[512];
if (metaData.get(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_AUTH,presentedAuth,sizeof(presentedAuth)) > 0) {
presentedAuth[511] = (char)0; // sanity check
-
- // Check for bearer token presented by member
if ((strlen(presentedAuth) > 6)&&(!strncmp(presentedAuth,"token:",6))) {
const char *const presentedToken = presentedAuth + 6;
-
- json &authTokens = network["authTokens"];
- if (authTokens.is_array()) {
- for(unsigned long i=0;i<authTokens.size();++i) {
- json &token = authTokens[i];
- if (token.is_object()) {
- const uint64_t expires = OSUtils::jsonInt(token["expires"],0ULL);
- const uint64_t maxUses = OSUtils::jsonInt(token["maxUsesPerMember"],0ULL);
- std::string tstr = OSUtils::jsonString(token["token"],"");
-
- if (((expires == 0ULL)||(expires > now))&&(tstr == presentedToken)) {
- bool usable = (maxUses == 0);
- if (!usable) {
- uint64_t useCount = 0;
- json &ahist = member["authHistory"];
- if (ahist.is_array()) {
- for(unsigned long j=0;j<ahist.size();++j) {
- json &ah = ahist[j];
- if ((OSUtils::jsonString(ah["ct"],"") == "token")&&(OSUtils::jsonString(ah["c"],"") == tstr)&&(OSUtils::jsonBool(ah["a"],false)))
- ++useCount;
- }
- }
- usable = (useCount < maxUses);
- }
- if (usable) {
- authorizedBy = "token";
- autoAuthorized = true;
- autoAuthCredentialType = "token";
- autoAuthCredential = tstr;
- }
- }
- }
+ json authTokens(network["authTokens"]);
+ json &tokenExpires = authTokens[presentedToken];
+ if (tokenExpires.is_number()) {
+ if ((tokenExpires == 0)||(tokenExpires > now)) {
+ authorized = true;
+ autoAuthorized = true;
+ autoAuthCredentialType = "token";
+ autoAuthCredential = presentedToken;
}
}
}
@@ -1354,58 +1271,42 @@ void EmbeddedNetworkController::_request(
}
// If we auto-authorized, update member record
- if ((autoAuthorized)&&(authorizedBy)) {
+ if ((autoAuthorized)&&(authorized)) {
member["authorized"] = true;
member["lastAuthorizedTime"] = now;
-
- json ah;
- ah["a"] = true;
- ah["by"] = authorizedBy;
- ah["ts"] = now;
- ah["ct"] = autoAuthCredentialType;
- ah["c"] = autoAuthCredential;
- member["authHistory"].push_back(ah);
-
- json &revj = member["revision"];
- member["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL);
+ member["lastAuthorizedCredentialType"] = autoAuthCredentialType;
+ member["lastAuthorizedCredential"] = autoAuthCredential;
}
- // Log this request
- if (requestPacketId) { // only log if this is a request, not for generated pushes
- json rlEntry = json::object();
- rlEntry["ts"] = now;
- rlEntry["auth"] = (authorizedBy) ? true : false;
- rlEntry["authBy"] = (authorizedBy) ? authorizedBy : "";
- rlEntry["vMajor"] = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION,0);
- rlEntry["vMinor"] = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION,0);
- rlEntry["vRev"] = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION,0);
- rlEntry["vProto"] = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_PROTOCOL_VERSION,0);
- if (fromAddr)
- rlEntry["fromAddr"] = fromAddr.toString();
-
- json recentLog = json::array();
- recentLog.push_back(rlEntry);
- json &oldLog = member["recentLog"];
- if (oldLog.is_array()) {
- for(unsigned long i=0;i<oldLog.size();++i) {
- recentLog.push_back(oldLog[i]);
- if (recentLog.size() >= ZT_NETCONF_DB_MEMBER_HISTORY_LENGTH)
- break;
- }
- }
- member["recentLog"] = recentLog;
+ if (authorized) {
+ // Update version info and meta-data if authorized and if this is a genuine request
+ if (requestPacketId) {
+ const uint64_t vMajor = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION,0);
+ const uint64_t vMinor = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION,0);
+ const uint64_t vRev = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION,0);
+ const uint64_t vProto = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_PROTOCOL_VERSION,0);
- // Also only do this on real requests
- member["lastRequestMetaData"] = metaData.data();
- }
+ member["vMajor"] = vMajor;
+ member["vMinor"] = vMinor;
+ member["vRev"] = vRev;
+ member["vProto"] = vProto;
- // If they are not authorized, STOP!
- if (!authorizedBy) {
- if (origMember != member) {
- member["lastModified"] = now;
- Mutex::Lock _l(_db_m);
- _db.put("network",nwids,"member",identity.address().toString(),member);
+ {
+ std::lock_guard<std::mutex> l(_memberStatus_l);
+ _MemberStatus &ms = _memberStatus[_MemberStatusKey(nwid,identity.address().toInt())];
+
+ ms.vMajor = (int)vMajor;
+ ms.vMinor = (int)vMinor;
+ ms.vRev = (int)vRev;
+ ms.vProto = (int)vProto;
+ ms.lastRequestMetaData = metaData;
+ ms.identity = identity;
+ }
}
+ } else {
+ // If they are not authorized, STOP!
+ DB::cleanMember(member);
+ _db->save(&origMember,member);
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED);
return;
}
@@ -1414,16 +1315,12 @@ void EmbeddedNetworkController::_request(
// If we made it this far, they are authorized.
// -------------------------------------------------------------------------
- NetworkConfig nc;
- _NetworkMemberInfo nmi;
- _getNetworkMemberInfo(now,nwid,nmi);
-
- uint64_t credentialtmd = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA;
- if (now > nmi.mostRecentDeauthTime) {
+ int64_t credentialtmd = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA;
+ if (now > ns.mostRecentDeauthTime) {
// If we recently de-authorized a member, shrink credential TTL/max delta to
// be below the threshold required to exclude it. Cap this to a min/max to
// prevent jitter or absurdly large values.
- const uint64_t deauthWindow = now - nmi.mostRecentDeauthTime;
+ const uint64_t deauthWindow = now - ns.mostRecentDeauthTime;
if (deauthWindow < ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA) {
credentialtmd = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA;
} else if (deauthWindow < (ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA + 5000ULL)) {
@@ -1431,21 +1328,36 @@ void EmbeddedNetworkController::_request(
}
}
- nc.networkId = nwid;
- nc.type = OSUtils::jsonBool(network["private"],true) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC;
- nc.timestamp = now;
- nc.credentialTimeMaxDelta = credentialtmd;
- nc.revision = OSUtils::jsonInt(network["revision"],0ULL);
- nc.issuedTo = identity.address();
- if (OSUtils::jsonBool(network["enableBroadcast"],true)) nc.flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST;
- if (OSUtils::jsonBool(network["allowPassiveBridging"],false)) nc.flags |= ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING;
- Utils::scopy(nc.name,sizeof(nc.name),OSUtils::jsonString(network["name"],"").c_str());
- nc.multicastLimit = (unsigned int)OSUtils::jsonInt(network["multicastLimit"],32ULL);
-
- for(std::set<Address>::const_iterator ab(nmi.activeBridges.begin());ab!=nmi.activeBridges.end();++ab) {
- nc.addSpecialist(*ab,ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE);
+ std::unique_ptr<NetworkConfig> nc(new NetworkConfig());
+
+ nc->networkId = nwid;
+ nc->type = OSUtils::jsonBool(network["private"],true) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC;
+ nc->timestamp = now;
+ nc->credentialTimeMaxDelta = credentialtmd;
+ nc->revision = OSUtils::jsonInt(network["revision"],0ULL);
+ nc->issuedTo = identity.address();
+ if (OSUtils::jsonBool(network["enableBroadcast"],true)) nc->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST;
+ Utils::scopy(nc->name,sizeof(nc->name),OSUtils::jsonString(network["name"],"").c_str());
+ nc->mtu = std::max(std::min((unsigned int)OSUtils::jsonInt(network["mtu"],ZT_DEFAULT_MTU),(unsigned int)ZT_MAX_MTU),(unsigned int)ZT_MIN_MTU);
+ nc->multicastLimit = (unsigned int)OSUtils::jsonInt(network["multicastLimit"],32ULL);
+
+ std::string rtt(OSUtils::jsonString(member["remoteTraceTarget"],""));
+ if (rtt.length() == 10) {
+ nc->remoteTraceTarget = Address(Utils::hexStrToU64(rtt.c_str()));
+ nc->remoteTraceLevel = (Trace::Level)OSUtils::jsonInt(member["remoteTraceLevel"],0ULL);
+ } else {
+ rtt = OSUtils::jsonString(network["remoteTraceTarget"],"");
+ if (rtt.length() == 10) {
+ nc->remoteTraceTarget = Address(Utils::hexStrToU64(rtt.c_str()));
+ } else {
+ nc->remoteTraceTarget.zero();
+ }
+ nc->remoteTraceLevel = (Trace::Level)OSUtils::jsonInt(network["remoteTraceLevel"],0ULL);
}
+ for(std::vector<Address>::const_iterator ab(ns.activeBridges.begin());ab!=ns.activeBridges.end();++ab)
+ nc->addSpecialist(*ab,ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE);
+
json &v4AssignMode = network["v4AssignMode"];
json &v6AssignMode = network["v6AssignMode"];
json &ipAssignmentPools = network["ipAssignmentPools"];
@@ -1460,15 +1372,15 @@ void EmbeddedNetworkController::_request(
// Old versions with no rules engine support get an allow everything rule.
// Since rules are enforced bidirectionally, newer versions *will* still
// enforce rules on the inbound side.
- nc.ruleCount = 1;
- nc.rules[0].t = ZT_NETWORK_RULE_ACTION_ACCEPT;
+ nc->ruleCount = 1;
+ nc->rules[0].t = ZT_NETWORK_RULE_ACTION_ACCEPT;
} else {
if (rules.is_array()) {
for(unsigned long i=0;i<rules.size();++i) {
- if (nc.ruleCount >= ZT_MAX_NETWORK_RULES)
+ if (nc->ruleCount >= ZT_MAX_NETWORK_RULES)
break;
- if (_parseRule(rules[i],nc.rules[nc.ruleCount]))
- ++nc.ruleCount;
+ if (_parseRule(rules[i],nc->rules[nc->ruleCount]))
+ ++nc->ruleCount;
}
}
@@ -1512,10 +1424,10 @@ void EmbeddedNetworkController::_request(
++caprc;
}
}
- nc.capabilities[nc.capabilityCount] = Capability((uint32_t)capId,nwid,now,1,capr,caprc);
- if (nc.capabilities[nc.capabilityCount].sign(_signingId,identity.address()))
- ++nc.capabilityCount;
- if (nc.capabilityCount >= ZT_MAX_NETWORK_CAPABILITIES)
+ nc->capabilities[nc->capabilityCount] = Capability((uint32_t)capId,nwid,now,1,capr,caprc);
+ if (nc->capabilities[nc->capabilityCount].sign(_signingId,identity.address()))
+ ++nc->capabilityCount;
+ if (nc->capabilityCount >= ZT_MAX_NETWORK_CAPABILITIES)
break;
}
}
@@ -1546,31 +1458,31 @@ void EmbeddedNetworkController::_request(
}
}
for(std::map< uint32_t,uint32_t >::const_iterator t(memberTagsById.begin());t!=memberTagsById.end();++t) {
- if (nc.tagCount >= ZT_MAX_NETWORK_TAGS)
+ if (nc->tagCount >= ZT_MAX_NETWORK_TAGS)
break;
- nc.tags[nc.tagCount] = Tag(nwid,now,identity.address(),t->first,t->second);
- if (nc.tags[nc.tagCount].sign(_signingId))
- ++nc.tagCount;
+ nc->tags[nc->tagCount] = Tag(nwid,now,identity.address(),t->first,t->second);
+ if (nc->tags[nc->tagCount].sign(_signingId))
+ ++nc->tagCount;
}
}
if (routes.is_array()) {
for(unsigned long i=0;i<routes.size();++i) {
- if (nc.routeCount >= ZT_MAX_NETWORK_ROUTES)
+ if (nc->routeCount >= ZT_MAX_NETWORK_ROUTES)
break;
json &route = routes[i];
json &target = route["target"];
json &via = route["via"];
if (target.is_string()) {
- const InetAddress t(target.get<std::string>());
+ const InetAddress t(target.get<std::string>().c_str());
InetAddress v;
- if (via.is_string()) v.fromString(via.get<std::string>());
+ if (via.is_string()) v.fromString(via.get<std::string>().c_str());
if ((t.ss_family == AF_INET)||(t.ss_family == AF_INET6)) {
- ZT_VirtualNetworkRoute *r = &(nc.routes[nc.routeCount]);
+ ZT_VirtualNetworkRoute *r = &(nc->routes[nc->routeCount]);
*(reinterpret_cast<InetAddress *>(&(r->target))) = t;
if (v.ss_family == t.ss_family)
*(reinterpret_cast<InetAddress *>(&(r->via))) = v;
- ++nc.routeCount;
+ ++nc->routeCount;
}
}
}
@@ -1579,13 +1491,13 @@ void EmbeddedNetworkController::_request(
const bool noAutoAssignIps = OSUtils::jsonBool(member["noAutoAssignIps"],false);
if ((v6AssignMode.is_object())&&(!noAutoAssignIps)) {
- if ((OSUtils::jsonBool(v6AssignMode["rfc4193"],false))&&(nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
- nc.staticIps[nc.staticIpCount++] = InetAddress::makeIpv6rfc4193(nwid,identity.address().toInt());
- nc.flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION;
+ if ((OSUtils::jsonBool(v6AssignMode["rfc4193"],false))&&(nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
+ nc->staticIps[nc->staticIpCount++] = InetAddress::makeIpv6rfc4193(nwid,identity.address().toInt());
+ nc->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION;
}
- if ((OSUtils::jsonBool(v6AssignMode["6plane"],false))&&(nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
- nc.staticIps[nc.staticIpCount++] = InetAddress::makeIpv66plane(nwid,identity.address().toInt());
- nc.flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION;
+ if ((OSUtils::jsonBool(v6AssignMode["6plane"],false))&&(nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
+ nc->staticIps[nc->staticIpCount++] = InetAddress::makeIpv66plane(nwid,identity.address().toInt());
+ nc->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION;
}
}
@@ -1594,29 +1506,29 @@ void EmbeddedNetworkController::_request(
json ipAssignments = member["ipAssignments"]; // we want to make a copy
if (ipAssignments.is_array()) {
for(unsigned long i=0;i<ipAssignments.size();++i) {
- if (!ipAssignments[i].is_string())
- continue;
- std::string ips = ipAssignments[i];
- InetAddress ip(ips);
-
- // IP assignments are only pushed if there is a corresponding local route. We also now get the netmask bits from
- // this route, ignoring the netmask bits field of the assigned IP itself. Using that was worthless and a source
- // of user error / poor UX.
- int routedNetmaskBits = 0;
- for(unsigned int rk=0;rk<nc.routeCount;++rk) {
- if ( (!nc.routes[rk].via.ss_family) && (reinterpret_cast<const InetAddress *>(&(nc.routes[rk].target))->containsAddress(ip)) )
- routedNetmaskBits = reinterpret_cast<const InetAddress *>(&(nc.routes[rk].target))->netmaskBits();
- }
+ if (ipAssignments[i].is_string()) {
+ const std::string ips = ipAssignments[i];
+ InetAddress ip(ips.c_str());
+
+ // IP assignments are only pushed if there is a corresponding local route. We also now get the netmask bits from
+ // this route, ignoring the netmask bits field of the assigned IP itself. Using that was worthless and a source
+ // of user error / poor UX.
+ int routedNetmaskBits = -1;
+ for(unsigned int rk=0;rk<nc->routeCount;++rk) {
+ if ( (!nc->routes[rk].via.ss_family) && (reinterpret_cast<const InetAddress *>(&(nc->routes[rk].target))->containsAddress(ip)) )
+ routedNetmaskBits = reinterpret_cast<const InetAddress *>(&(nc->routes[rk].target))->netmaskBits();
+ }
- if (routedNetmaskBits > 0) {
- if (nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) {
- ip.setPort(routedNetmaskBits);
- nc.staticIps[nc.staticIpCount++] = ip;
+ if (routedNetmaskBits >= 0) {
+ if (nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) {
+ ip.setPort(routedNetmaskBits);
+ nc->staticIps[nc->staticIpCount++] = ip;
+ }
+ if (ip.ss_family == AF_INET)
+ haveManagedIpv4AutoAssignment = true;
+ else if (ip.ss_family == AF_INET6)
+ haveManagedIpv6AutoAssignment = true;
}
- if (ip.ss_family == AF_INET)
- haveManagedIpv4AutoAssignment = true;
- else if (ip.ss_family == AF_INET6)
- haveManagedIpv6AutoAssignment = true;
}
}
} else {
@@ -1627,12 +1539,12 @@ void EmbeddedNetworkController::_request(
for(unsigned long p=0;((p<ipAssignmentPools.size())&&(!haveManagedIpv6AutoAssignment));++p) {
json &pool = ipAssignmentPools[p];
if (pool.is_object()) {
- InetAddress ipRangeStart(OSUtils::jsonString(pool["ipRangeStart"],""));
- InetAddress ipRangeEnd(OSUtils::jsonString(pool["ipRangeEnd"],""));
+ InetAddress ipRangeStart(OSUtils::jsonString(pool["ipRangeStart"],"").c_str());
+ InetAddress ipRangeEnd(OSUtils::jsonString(pool["ipRangeEnd"],"").c_str());
if ( (ipRangeStart.ss_family == AF_INET6) && (ipRangeEnd.ss_family == AF_INET6) ) {
uint64_t s[2],e[2],x[2],xx[2];
- memcpy(s,ipRangeStart.rawIpData(),16);
- memcpy(e,ipRangeEnd.rawIpData(),16);
+ ZT_FAST_MEMCPY(s,ipRangeStart.rawIpData(),16);
+ ZT_FAST_MEMCPY(e,ipRangeEnd.rawIpData(),16);
s[0] = Utils::ntoh(s[0]);
s[1] = Utils::ntoh(s[1]);
e[0] = Utils::ntoh(e[0]);
@@ -1662,21 +1574,24 @@ void EmbeddedNetworkController::_request(
// Check if this IP is within a local-to-Ethernet routed network
int routedNetmaskBits = 0;
- for(unsigned int rk=0;rk<nc.routeCount;++rk) {
- if ( (!nc.routes[rk].via.ss_family) && (nc.routes[rk].target.ss_family == AF_INET6) && (reinterpret_cast<const InetAddress *>(&(nc.routes[rk].target))->containsAddress(ip6)) )
- routedNetmaskBits = reinterpret_cast<const InetAddress *>(&(nc.routes[rk].target))->netmaskBits();
+ for(unsigned int rk=0;rk<nc->routeCount;++rk) {
+ if ( (!nc->routes[rk].via.ss_family) && (nc->routes[rk].target.ss_family == AF_INET6) && (reinterpret_cast<const InetAddress *>(&(nc->routes[rk].target))->containsAddress(ip6)) )
+ routedNetmaskBits = reinterpret_cast<const InetAddress *>(&(nc->routes[rk].target))->netmaskBits();
}
// If it's routed, then try to claim and assign it and if successful end loop
- if ((routedNetmaskBits > 0)&&(!nmi.allocatedIps.count(ip6))) {
- ipAssignments.push_back(ip6.toIpString());
- member["ipAssignments"] = ipAssignments;
- ip6.setPort((unsigned int)routedNetmaskBits);
- if (nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)
- nc.staticIps[nc.staticIpCount++] = ip6;
- haveManagedIpv6AutoAssignment = true;
- _clearNetworkMemberInfoCache(nwid); // clear cache to prevent IP assignment duplication on many rapid assigns
- break;
+ if ( (routedNetmaskBits > 0) && (!std::binary_search(ns.allocatedIps.begin(),ns.allocatedIps.end(),ip6)) ) {
+ char tmpip[64];
+ const std::string ipStr(ip6.toIpString(tmpip));
+ if (std::find(ipAssignments.begin(),ipAssignments.end(),ipStr) == ipAssignments.end()) {
+ ipAssignments.push_back(ipStr);
+ member["ipAssignments"] = ipAssignments;
+ ip6.setPort((unsigned int)routedNetmaskBits);
+ if (nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)
+ nc->staticIps[nc->staticIpCount++] = ip6;
+ haveManagedIpv6AutoAssignment = true;
+ break;
+ }
}
}
}
@@ -1688,8 +1603,8 @@ void EmbeddedNetworkController::_request(
for(unsigned long p=0;((p<ipAssignmentPools.size())&&(!haveManagedIpv4AutoAssignment));++p) {
json &pool = ipAssignmentPools[p];
if (pool.is_object()) {
- InetAddress ipRangeStartIA(OSUtils::jsonString(pool["ipRangeStart"],""));
- InetAddress ipRangeEndIA(OSUtils::jsonString(pool["ipRangeEnd"],""));
+ InetAddress ipRangeStartIA(OSUtils::jsonString(pool["ipRangeStart"],"").c_str());
+ InetAddress ipRangeEndIA(OSUtils::jsonString(pool["ipRangeEnd"],"").c_str());
if ( (ipRangeStartIA.ss_family == AF_INET) && (ipRangeEndIA.ss_family == AF_INET) ) {
uint32_t ipRangeStart = Utils::ntoh((uint32_t)(reinterpret_cast<struct sockaddr_in *>(&ipRangeStartIA)->sin_addr.s_addr));
uint32_t ipRangeEnd = Utils::ntoh((uint32_t)(reinterpret_cast<struct sockaddr_in *>(&ipRangeEndIA)->sin_addr.s_addr));
@@ -1708,10 +1623,10 @@ void EmbeddedNetworkController::_request(
// Check if this IP is within a local-to-Ethernet routed network
int routedNetmaskBits = -1;
- for(unsigned int rk=0;rk<nc.routeCount;++rk) {
- if (nc.routes[rk].target.ss_family == AF_INET) {
- uint32_t targetIp = Utils::ntoh((uint32_t)(reinterpret_cast<const struct sockaddr_in *>(&(nc.routes[rk].target))->sin_addr.s_addr));
- int targetBits = Utils::ntoh((uint16_t)(reinterpret_cast<const struct sockaddr_in *>(&(nc.routes[rk].target))->sin_port));
+ for(unsigned int rk=0;rk<nc->routeCount;++rk) {
+ if (nc->routes[rk].target.ss_family == AF_INET) {
+ uint32_t targetIp = Utils::ntoh((uint32_t)(reinterpret_cast<const struct sockaddr_in *>(&(nc->routes[rk].target))->sin_addr.s_addr));
+ int targetBits = Utils::ntoh((uint16_t)(reinterpret_cast<const struct sockaddr_in *>(&(nc->routes[rk].target))->sin_port));
if ((ip & (0xffffffff << (32 - targetBits))) == targetIp) {
routedNetmaskBits = targetBits;
break;
@@ -1721,18 +1636,21 @@ void EmbeddedNetworkController::_request(
// If it's routed, then try to claim and assign it and if successful end loop
const InetAddress ip4(Utils::hton(ip),0);
- if ((routedNetmaskBits > 0)&&(!nmi.allocatedIps.count(ip4))) {
- ipAssignments.push_back(ip4.toIpString());
- member["ipAssignments"] = ipAssignments;
- if (nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) {
- struct sockaddr_in *const v4ip = reinterpret_cast<struct sockaddr_in *>(&(nc.staticIps[nc.staticIpCount++]));
- v4ip->sin_family = AF_INET;
- v4ip->sin_port = Utils::hton((uint16_t)routedNetmaskBits);
- v4ip->sin_addr.s_addr = Utils::hton(ip);
+ if ( (routedNetmaskBits > 0) && (!std::binary_search(ns.allocatedIps.begin(),ns.allocatedIps.end(),ip4)) ) {
+ char tmpip[64];
+ const std::string ipStr(ip4.toIpString(tmpip));
+ if (std::find(ipAssignments.begin(),ipAssignments.end(),ipStr) == ipAssignments.end()) {
+ ipAssignments.push_back(ipStr);
+ member["ipAssignments"] = ipAssignments;
+ if (nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) {
+ struct sockaddr_in *const v4ip = reinterpret_cast<struct sockaddr_in *>(&(nc->staticIps[nc->staticIpCount++]));
+ v4ip->sin_family = AF_INET;
+ v4ip->sin_port = Utils::hton((uint16_t)routedNetmaskBits);
+ v4ip->sin_addr.s_addr = Utils::hton(ip);
+ }
+ haveManagedIpv4AutoAssignment = true;
+ break;
}
- haveManagedIpv4AutoAssignment = true;
- _clearNetworkMemberInfoCache(nwid); // clear cache to prevent IP assignment duplication on many rapid assigns
- break;
}
}
}
@@ -1741,114 +1659,52 @@ void EmbeddedNetworkController::_request(
}
// Issue a certificate of ownership for all static IPs
- if (nc.staticIpCount) {
- nc.certificatesOfOwnership[0] = CertificateOfOwnership(nwid,now,identity.address(),1);
- for(unsigned int i=0;i<nc.staticIpCount;++i)
- nc.certificatesOfOwnership[0].addThing(nc.staticIps[i]);
- nc.certificatesOfOwnership[0].sign(_signingId);
- nc.certificateOfOwnershipCount = 1;
+ if (nc->staticIpCount) {
+ nc->certificatesOfOwnership[0] = CertificateOfOwnership(nwid,now,identity.address(),1);
+ for(unsigned int i=0;i<nc->staticIpCount;++i)
+ nc->certificatesOfOwnership[0].addThing(nc->staticIps[i]);
+ nc->certificatesOfOwnership[0].sign(_signingId);
+ nc->certificateOfOwnershipCount = 1;
}
CertificateOfMembership com(now,credentialtmd,nwid,identity.address());
if (com.sign(_signingId)) {
- nc.com = com;
+ nc->com = com;
} else {
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_INTERNAL_SERVER_ERROR);
return;
}
- if (member != origMember) {
- member["lastModified"] = now;
- Mutex::Lock _l(_db_m);
- _db.put("network",nwids,"member",identity.address().toString(),member);
- }
-
- _sender->ncSendConfig(nwid,requestPacketId,identity.address(),nc,metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6);
+ DB::cleanMember(member);
+ _db->save(&origMember,member);
+ _sender->ncSendConfig(nwid,requestPacketId,identity.address(),*(nc.get()),metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6);
}
-void EmbeddedNetworkController::_getNetworkMemberInfo(uint64_t now,uint64_t nwid,_NetworkMemberInfo &nmi)
+void EmbeddedNetworkController::_startThreads()
{
- char pfx[256];
- Utils::snprintf(pfx,sizeof(pfx),"network/%.16llx/member",nwid);
-
- {
- Mutex::Lock _l(_nmiCache_m);
- std::map<uint64_t,_NetworkMemberInfo>::iterator c(_nmiCache.find(nwid));
- if ((c != _nmiCache.end())&&((now - c->second.nmiTimestamp) < 1000)) { // a short duration cache but limits CPU use on big networks
- nmi = c->second;
- return;
- }
- }
-
- {
- Mutex::Lock _l(_db_m);
- _db.filter(pfx,[&nmi,&now](const std::string &n,const json &member) {
- try {
- if (OSUtils::jsonBool(member["authorized"],false)) {
- ++nmi.authorizedMemberCount;
-
- if (member.count("recentLog")) {
- const json &mlog = member["recentLog"];
- if ((mlog.is_array())&&(mlog.size() > 0)) {
- const json &mlog1 = mlog[0];
- if (mlog1.is_object()) {
- if ((now - OSUtils::jsonInt(mlog1["ts"],0ULL)) < ZT_NETCONF_NODE_ACTIVE_THRESHOLD)
- ++nmi.activeMemberCount;
- }
- }
- }
-
- if (OSUtils::jsonBool(member["activeBridge"],false)) {
- nmi.activeBridges.insert(Address(Utils::hexStrToU64(OSUtils::jsonString(member["id"],"0000000000").c_str())));
- }
-
- if (member.count("ipAssignments")) {
- const json &mips = member["ipAssignments"];
- if (mips.is_array()) {
- for(unsigned long i=0;i<mips.size();++i) {
- InetAddress mip(OSUtils::jsonString(mips[i],""));
- if ((mip.ss_family == AF_INET)||(mip.ss_family == AF_INET6))
- nmi.allocatedIps.insert(mip);
- }
- }
+ std::lock_guard<std::mutex> l(_threads_l);
+ if (!_threads.empty())
+ return;
+ const long hwc = std::max((long)std::thread::hardware_concurrency(),(long)1);
+ for(long t=0;t<hwc;++t) {
+ _threads.emplace_back([this]() {
+ for(;;) {
+ _RQEntry *qe = (_RQEntry *)0;
+ if (!_queue.get(qe))
+ break;
+ try {
+ if (qe) {
+ _request(qe->nwid,qe->fromAddr,qe->requestPacketId,qe->identity,qe->metaData);
+ delete qe;
}
- } else {
- nmi.mostRecentDeauthTime = std::max(nmi.mostRecentDeauthTime,OSUtils::jsonInt(member["lastDeauthorizedTime"],0ULL));
+ } catch (std::exception &e) {
+ fprintf(stderr,"ERROR: exception in controller request handling thread: %s" ZT_EOL_S,e.what());
+ } catch ( ... ) {
+ fprintf(stderr,"ERROR: exception in controller request handling thread: unknown exception" ZT_EOL_S);
}
- } catch ( ... ) {}
- return true;
+ }
});
}
- nmi.nmiTimestamp = now;
-
- {
- Mutex::Lock _l(_nmiCache_m);
- _nmiCache[nwid] = nmi;
- }
-}
-
-void EmbeddedNetworkController::_pushMemberUpdate(uint64_t now,uint64_t nwid,const nlohmann::json &member)
-{
- try {
- const std::string idstr = member["identity"];
- const std::string mdstr = member["lastRequestMetaData"];
- if ((idstr.length() > 0)&&(mdstr.length() > 0)) {
- const Identity id(idstr);
- bool online;
- {
- Mutex::Lock _l(_lastRequestTime_m);
- std::map< std::pair<uint64_t,uint64_t>,uint64_t >::iterator lrt(_lastRequestTime.find(std::pair<uint64_t,uint64_t>(id.address().toInt(),nwid)));
- online = ( (lrt != _lastRequestTime.end()) && ((now - lrt->second) < ZT_NETWORK_AUTOCONF_DELAY) );
- }
- if (online) {
- Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> *metaData = new Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY>(mdstr.c_str());
- try {
- this->request(nwid,InetAddress(),0,id,*metaData);
- } catch ( ... ) {}
- delete metaData;
- }
- }
- } catch ( ... ) {}
}
} // namespace ZeroTier
diff --git a/controller/EmbeddedNetworkController.hpp b/controller/EmbeddedNetworkController.hpp
index 0ae2f3b5..417005a4 100644
--- a/controller/EmbeddedNetworkController.hpp
+++ b/controller/EmbeddedNetworkController.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2015 ZeroTier, Inc.
+ * Copyright (C) 2011-2018 ZeroTier, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,11 +26,12 @@
#include <vector>
#include <set>
#include <list>
+#include <thread>
+#include <unordered_map>
+#include <atomic>
#include "../node/Constants.hpp"
-
#include "../node/NetworkController.hpp"
-#include "../node/Mutex.hpp"
#include "../node/Utils.hpp"
#include "../node/Address.hpp"
#include "../node/InetAddress.hpp"
@@ -41,13 +42,11 @@
#include "../ext/json/json.hpp"
-#include "JSONDB.hpp"
-
-// Number of background threads to start -- not actually started until needed
-#define ZT_EMBEDDEDNETWORKCONTROLLER_BACKGROUND_THREAD_COUNT 4
-
-// TTL for circuit tests
-#define ZT_EMBEDDEDNETWORKCONTROLLER_CIRCUIT_TEST_EXPIRATION 120000
+#include "DB.hpp"
+#include "FileDB.hpp"
+#ifdef ZT_CONTROLLER_USE_RETHINKDB
+#include "RethinkDB.hpp"
+#endif
namespace ZeroTier {
@@ -58,7 +57,7 @@ class EmbeddedNetworkController : public NetworkController
public:
/**
* @param node Parent node
- * @param dbPath Path to store data
+ * @param dbPath Database path (file path or database credentials)
*/
EmbeddedNetworkController(Node *node,const char *dbPath);
virtual ~EmbeddedNetworkController();
@@ -94,10 +93,17 @@ public:
std::string &responseBody,
std::string &responseContentType);
- void threadMain()
- throw();
+ void handleRemoteTrace(const ZT_RemoteTrace &rt);
+
+ // Called on update via POST or by JSONDB on external update of network or network member records
+ void onNetworkUpdate(const uint64_t networkId);
+ void onNetworkMemberUpdate(const uint64_t networkId,const uint64_t memberId);
+ void onNetworkMemberDeauthorize(const uint64_t networkId,const uint64_t memberId);
private:
+ void _request(uint64_t nwid,const InetAddress &fromAddr,uint64_t requestPacketId,const Identity &identity,const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> &metaData);
+ void _startThreads();
+
struct _RQEntry
{
uint64_t nwid;
@@ -105,104 +111,47 @@ private:
InetAddress fromAddr;
Identity identity;
Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> metaData;
+ enum {
+ RQENTRY_TYPE_REQUEST = 0
+ } type;
};
-
- // Gathers a bunch of statistics about members of a network, IP assignments, etc. that we need in various places
- struct _NetworkMemberInfo
+ struct _MemberStatusKey
{
- _NetworkMemberInfo() : authorizedMemberCount(0),activeMemberCount(0),totalMemberCount(0),mostRecentDeauthTime(0) {}
- std::set<Address> activeBridges;
- std::set<InetAddress> allocatedIps;
- unsigned long authorizedMemberCount;
- unsigned long activeMemberCount;
- unsigned long totalMemberCount;
- uint64_t mostRecentDeauthTime;
- uint64_t nmiTimestamp; // time this NMI structure was computed
+ _MemberStatusKey() : networkId(0),nodeId(0) {}
+ _MemberStatusKey(const uint64_t nwid,const uint64_t nid) : networkId(nwid),nodeId(nid) {}
+ uint64_t networkId;
+ uint64_t nodeId;
+ inline bool operator==(const _MemberStatusKey &k) const { return ((k.networkId == networkId)&&(k.nodeId == nodeId)); }
};
-
- static void _circuitTestCallback(ZT_Node *node,ZT_CircuitTest *test,const ZT_CircuitTestReport *report);
- void _request(uint64_t nwid,const InetAddress &fromAddr,uint64_t requestPacketId,const Identity &identity,const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> &metaData);
- void _getNetworkMemberInfo(uint64_t now,uint64_t nwid,_NetworkMemberInfo &nmi);
- inline void _clearNetworkMemberInfoCache(const uint64_t nwid) { Mutex::Lock _l(_nmiCache_m); _nmiCache.erase(nwid); }
- void _pushMemberUpdate(uint64_t now,uint64_t nwid,const nlohmann::json &member);
-
- // These init objects with default and static/informational fields
- inline void _initMember(nlohmann::json &member)
+ struct _MemberStatus
{
- if (!member.count("authorized")) member["authorized"] = false;
- if (!member.count("authHistory")) member["authHistory"] = nlohmann::json::array();
- if (!member.count("ipAssignments")) member["ipAssignments"] = nlohmann::json::array();
- if (!member.count("recentLog")) member["recentLog"] = nlohmann::json::array();
- if (!member.count("activeBridge")) member["activeBridge"] = false;
- if (!member.count("tags")) member["tags"] = nlohmann::json::array();
- if (!member.count("capabilities")) member["capabilities"] = nlohmann::json::array();
- if (!member.count("creationTime")) member["creationTime"] = OSUtils::now();
- if (!member.count("noAutoAssignIps")) member["noAutoAssignIps"] = false;
- if (!member.count("revision")) member["revision"] = 0ULL;
- if (!member.count("lastDeauthorizedTime")) member["lastDeauthorizedTime"] = 0ULL;
- if (!member.count("lastAuthorizedTime")) member["lastAuthorizedTime"] = 0ULL;
- member["objtype"] = "member";
- }
- inline void _initNetwork(nlohmann::json &network)
+ _MemberStatus() : lastRequestTime(0),vMajor(-1),vMinor(-1),vRev(-1),vProto(-1) {}
+ uint64_t lastRequestTime;
+ int vMajor,vMinor,vRev,vProto;
+ Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> lastRequestMetaData;
+ Identity identity;
+ inline bool online(const int64_t now) const { return ((now - lastRequestTime) < (ZT_NETWORK_AUTOCONF_DELAY * 2)); }
+ };
+ struct _MemberStatusHash
{
- if (!network.count("private")) network["private"] = true;
- if (!network.count("creationTime")) network["creationTime"] = OSUtils::now();
- if (!network.count("name")) network["name"] = "";
- if (!network.count("multicastLimit")) network["multicastLimit"] = (uint64_t)32;
- if (!network.count("enableBroadcast")) network["enableBroadcast"] = true;
- if (!network.count("v4AssignMode")) network["v4AssignMode"] = {{"zt",false}};
- if (!network.count("v6AssignMode")) network["v6AssignMode"] = {{"rfc4193",false},{"zt",false},{"6plane",false}};
- if (!network.count("authTokens")) network["authTokens"] = nlohmann::json::array();
- if (!network.count("capabilities")) network["capabilities"] = nlohmann::json::array();
- if (!network.count("tags")) network["tags"] = nlohmann::json::array();
- if (!network.count("routes")) network["routes"] = nlohmann::json::array();
- if (!network.count("ipAssignmentPools")) network["ipAssignmentPools"] = nlohmann::json::array();
- if (!network.count("rules")) {
- // If unspecified, rules are set to allow anything and behave like a flat L2 segment
- network["rules"] = {{
- { "not",false },
- { "or", false },
- { "type","ACTION_ACCEPT" }
- }};
+ inline std::size_t operator()(const _MemberStatusKey &networkIdNodeId) const
+ {
+ return (std::size_t)(networkIdNodeId.networkId + networkIdNodeId.nodeId);
}
- network["objtype"] = "network";
- }
- inline void _addNetworkNonPersistedFields(nlohmann::json &network,uint64_t now,const _NetworkMemberInfo &nmi)
- {
- network["clock"] = now;
- network["authorizedMemberCount"] = nmi.authorizedMemberCount;
- network["activeMemberCount"] = nmi.activeMemberCount;
- network["totalMemberCount"] = nmi.totalMemberCount;
- }
- inline void _addMemberNonPersistedFields(nlohmann::json &member,uint64_t now)
- {
- member["clock"] = now;
- }
-
- const uint64_t _startTime;
-
- BlockingQueue<_RQEntry *> _queue;
- Thread _threads[ZT_EMBEDDEDNETWORKCONTROLLER_BACKGROUND_THREAD_COUNT];
- bool _threadsStarted;
- Mutex _threads_m;
-
- std::map<uint64_t,_NetworkMemberInfo> _nmiCache;
- Mutex _nmiCache_m;
-
- JSONDB _db;
- Mutex _db_m;
+ };
+ const int64_t _startTime;
Node *const _node;
std::string _path;
-
- NetworkController::Sender *_sender;
Identity _signingId;
-
- std::list< ZT_CircuitTest > _tests;
- Mutex _tests_m;
-
- std::map< std::pair<uint64_t,uint64_t>,uint64_t > _lastRequestTime; // last request time by <address,networkId>
- Mutex _lastRequestTime_m;
+ std::string _signingIdAddressString;
+ NetworkController::Sender *_sender;
+ std::unique_ptr<DB> _db;
+ BlockingQueue< _RQEntry * > _queue;
+ std::vector<std::thread> _threads;
+ std::mutex _threads_l;
+ std::unordered_map< _MemberStatusKey,_MemberStatus,_MemberStatusHash > _memberStatus;
+ std::mutex _memberStatus_l;
};
} // namespace ZeroTier
diff --git a/controller/FileDB.cpp b/controller/FileDB.cpp
new file mode 100644
index 00000000..a7b59cbf
--- /dev/null
+++ b/controller/FileDB.cpp
@@ -0,0 +1,150 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "FileDB.hpp"
+
+namespace ZeroTier
+{
+
+FileDB::FileDB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path) :
+ DB(nc,myId,path),
+ _networksPath(_path + ZT_PATH_SEPARATOR_S + "network"),
+ _tracePath(_path + ZT_PATH_SEPARATOR_S + "trace")
+{
+ OSUtils::mkdir(_path.c_str());
+ OSUtils::lockDownFile(_path.c_str(),true);
+ OSUtils::mkdir(_networksPath.c_str());
+ OSUtils::mkdir(_tracePath.c_str());
+
+ std::vector<std::string> networks(OSUtils::listDirectory(_networksPath.c_str(),false));
+ std::string buf;
+ for(auto n=networks.begin();n!=networks.end();++n) {
+ buf.clear();
+ if ((n->length() == 21)&&(OSUtils::readFile((_networksPath + ZT_PATH_SEPARATOR_S + *n).c_str(),buf))) {
+ try {
+ nlohmann::json network(OSUtils::jsonParse(buf));
+ const std::string nwids = network["id"];
+ if (nwids.length() == 16) {
+ nlohmann::json nullJson;
+ _networkChanged(nullJson,network,false);
+ std::string membersPath(_networksPath + ZT_PATH_SEPARATOR_S + nwids + ZT_PATH_SEPARATOR_S "member");
+ std::vector<std::string> members(OSUtils::listDirectory(membersPath.c_str(),false));
+ for(auto m=members.begin();m!=members.end();++m) {
+ buf.clear();
+ if ((m->length() == 15)&&(OSUtils::readFile((membersPath + ZT_PATH_SEPARATOR_S + *m).c_str(),buf))) {
+ try {
+ nlohmann::json member(OSUtils::jsonParse(buf));
+ const std::string addrs = member["id"];
+ if (addrs.length() == 10) {
+ nlohmann::json nullJson2;
+ _memberChanged(nullJson2,member,false);
+ }
+ } catch ( ... ) {}
+ }
+ }
+ }
+ } catch ( ... ) {}
+ }
+ }
+}
+
+FileDB::~FileDB()
+{
+}
+
+bool FileDB::waitForReady()
+{
+ return true;
+}
+
+void FileDB::save(nlohmann::json *orig,nlohmann::json &record)
+{
+ char p1[4096],p2[4096],pb[4096];
+ try {
+ if (orig) {
+ if (*orig != record) {
+ record["revision"] = OSUtils::jsonInt(record["revision"],0ULL) + 1;
+ }
+ } else {
+ record["revision"] = 1;
+ }
+
+ const std::string objtype = record["objtype"];
+ if (objtype == "network") {
+ const uint64_t nwid = OSUtils::jsonIntHex(record["id"],0ULL);
+ if (nwid) {
+ nlohmann::json old;
+ get(nwid,old);
+
+ OSUtils::ztsnprintf(p1,sizeof(p1),"%s" ZT_PATH_SEPARATOR_S "%.16llx.json.new",_networksPath.c_str(),nwid);
+ OSUtils::ztsnprintf(p2,sizeof(p2),"%s" ZT_PATH_SEPARATOR_S "%.16llx.json",_networksPath.c_str(),nwid);
+ if (!OSUtils::writeFile(p1,OSUtils::jsonDump(record,-1)))
+ fprintf(stderr,"WARNING: controller unable to write to path: %s" ZT_EOL_S,p1);
+ OSUtils::rename(p1,p2);
+
+ _networkChanged(old,record,true);
+ }
+ } else if (objtype == "member") {
+ const uint64_t id = OSUtils::jsonIntHex(record["id"],0ULL);
+ const uint64_t nwid = OSUtils::jsonIntHex(record["nwid"],0ULL);
+ if ((id)&&(nwid)) {
+ nlohmann::json network,old;
+ get(nwid,network,id,old);
+
+ OSUtils::ztsnprintf(pb,sizeof(pb),"%s" ZT_PATH_SEPARATOR_S "%.16llx" ZT_PATH_SEPARATOR_S "member",_networksPath.c_str(),(unsigned long long)nwid);
+ OSUtils::ztsnprintf(p1,sizeof(p1),"%s" ZT_PATH_SEPARATOR_S "%.10llx.json.new",pb,(unsigned long long)id);
+ OSUtils::ztsnprintf(p2,sizeof(p2),"%s" ZT_PATH_SEPARATOR_S "%.10llx.json",pb,(unsigned long long)id);
+ if (!OSUtils::writeFile(p1,OSUtils::jsonDump(record,-1))) {
+ OSUtils::mkdir(pb);
+ if (!OSUtils::writeFile(p1,OSUtils::jsonDump(record,-1)))
+ fprintf(stderr,"WARNING: controller unable to write to path: %s" ZT_EOL_S,p1);
+ }
+ OSUtils::rename(p1,p2);
+
+ _memberChanged(old,record,true);
+ }
+ } else if (objtype == "trace") {
+ const std::string id = record["id"];
+ if (id.length() > 0) {
+ OSUtils::ztsnprintf(p1,sizeof(p1),"%s" ZT_PATH_SEPARATOR_S "%s.json",_tracePath.c_str(),id.c_str());
+ OSUtils::writeFile(p1,OSUtils::jsonDump(record,-1));
+ }
+ }
+ } catch ( ... ) {} // drop invalid records missing fields
+}
+
+void FileDB::eraseNetwork(const uint64_t networkId)
+{
+ nlohmann::json network,nullJson;
+ get(networkId,network);
+ char p[16384];
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.16llx.json",_networksPath.c_str(),networkId);
+ OSUtils::rm(p);
+ _networkChanged(network,nullJson,true);
+}
+
+void FileDB::eraseMember(const uint64_t networkId,const uint64_t memberId)
+{
+}
+
+void FileDB::nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress)
+{
+ // Nothing to do here right now in the filesystem store mode since we can just get this from the peer list
+}
+
+} // namespace ZeroTier
diff --git a/controller/FileDB.hpp b/controller/FileDB.hpp
new file mode 100644
index 00000000..1e275a36
--- /dev/null
+++ b/controller/FileDB.hpp
@@ -0,0 +1,46 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ZT_CONTROLLER_FILEDB_HPP
+#define ZT_CONTROLLER_FILEDB_HPP
+
+#include "DB.hpp"
+
+namespace ZeroTier
+{
+
+class FileDB : public DB
+{
+public:
+ FileDB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path);
+ virtual ~FileDB();
+
+ virtual bool waitForReady();
+ virtual void save(nlohmann::json *orig,nlohmann::json &record);
+ virtual void eraseNetwork(const uint64_t networkId);
+ virtual void eraseMember(const uint64_t networkId,const uint64_t memberId);
+ virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress);
+
+protected:
+ std::string _networksPath;
+ std::string _tracePath;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/controller/JSONDB.cpp b/controller/JSONDB.cpp
deleted file mode 100644
index d3e76fc1..00000000
--- a/controller/JSONDB.cpp
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2015 ZeroTier, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "JSONDB.hpp"
-
-#define ZT_JSONDB_HTTP_TIMEOUT 60000
-
-namespace ZeroTier {
-
-static const nlohmann::json _EMPTY_JSON(nlohmann::json::object());
-static const std::map<std::string,std::string> _ZT_JSONDB_GET_HEADERS;
-
-JSONDB::JSONDB(const std::string &basePath) :
- _basePath(basePath),
- _ready(false)
-{
- if ((_basePath.length() > 7)&&(_basePath.substr(0,7) == "http://")) {
- // TODO: this doesn't yet support IPv6 since bracketed address notiation isn't supported.
- // Typically it's used with 127.0.0.1 anyway.
- std::string hn = _basePath.substr(7);
- std::size_t hnend = hn.find_first_of('/');
- if (hnend != std::string::npos)
- hn = hn.substr(0,hnend);
- std::size_t hnsep = hn.find_last_of(':');
- if (hnsep != std::string::npos)
- hn[hnsep] = '/';
- _httpAddr.fromString(hn);
- if (hnend != std::string::npos)
- _basePath = _basePath.substr(7 + hnend);
- if (_basePath.length() == 0)
- _basePath = "/";
- if (_basePath[0] != '/')
- _basePath = std::string("/") + _basePath;
- } else {
- OSUtils::mkdir(_basePath.c_str());
- OSUtils::lockDownFile(_basePath.c_str(),true); // networks might contain auth tokens, etc., so restrict directory permissions
- }
- _ready = _reload(_basePath,std::string());
-}
-
-bool JSONDB::writeRaw(const std::string &n,const std::string &obj)
-{
- if (!_isValidObjectName(n))
- return false;
- if (_httpAddr) {
- std::map<std::string,std::string> headers;
- std::string body;
- std::map<std::string,std::string> reqHeaders;
- char tmp[64];
- Utils::snprintf(tmp,sizeof(tmp),"%lu",(unsigned long)obj.length());
- reqHeaders["Content-Length"] = tmp;
- reqHeaders["Content-Type"] = "application/json";
- const unsigned int sc = Http::PUT(1048576,ZT_JSONDB_HTTP_TIMEOUT,reinterpret_cast<const struct sockaddr *>(&_httpAddr),(_basePath+"/"+n).c_str(),reqHeaders,obj.data(),(unsigned long)obj.length(),headers,body);
- return (sc == 200);
- } else {
- const std::string path(_genPath(n,true));
- if (!path.length())
- return false;
- return OSUtils::writeFile(path.c_str(),obj);
- }
-}
-
-bool JSONDB::put(const std::string &n,const nlohmann::json &obj)
-{
- const bool r = writeRaw(n,OSUtils::jsonDump(obj));
- _db[n].obj = obj;
- return r;
-}
-
-const nlohmann::json &JSONDB::get(const std::string &n)
-{
- while (!_ready) {
- Thread::sleep(250);
- _ready = _reload(_basePath,std::string());
- }
-
- if (!_isValidObjectName(n))
- return _EMPTY_JSON;
- std::map<std::string,_E>::iterator e(_db.find(n));
- if (e != _db.end())
- return e->second.obj;
-
- std::string buf;
- if (_httpAddr) {
- std::map<std::string,std::string> headers;
- const unsigned int sc = Http::GET(1048576,ZT_JSONDB_HTTP_TIMEOUT,reinterpret_cast<const struct sockaddr *>(&_httpAddr),(_basePath+"/"+n).c_str(),_ZT_JSONDB_GET_HEADERS,headers,buf);
- if (sc != 200)
- return _EMPTY_JSON;
- } else {
- const std::string path(_genPath(n,false));
- if (!path.length())
- return _EMPTY_JSON;
- if (!OSUtils::readFile(path.c_str(),buf))
- return _EMPTY_JSON;
- }
-
- try {
- _E &e2 = _db[n];
- e2.obj = OSUtils::jsonParse(buf);
- return e2.obj;
- } catch ( ... ) {
- _db.erase(n);
- return _EMPTY_JSON;
- }
-}
-
-void JSONDB::erase(const std::string &n)
-{
- if (!_isValidObjectName(n))
- return;
-
- if (_httpAddr) {
- std::string body;
- std::map<std::string,std::string> headers;
- Http::DEL(1048576,ZT_JSONDB_HTTP_TIMEOUT,reinterpret_cast<const struct sockaddr *>(&_httpAddr),(_basePath+"/"+n).c_str(),_ZT_JSONDB_GET_HEADERS,headers,body);
- } else {
- std::string path(_genPath(n,true));
- if (!path.length())
- return;
- OSUtils::rm(path.c_str());
- }
-
- _db.erase(n);
-}
-
-bool JSONDB::_reload(const std::string &p,const std::string &b)
-{
- if (_httpAddr) {
- std::string body;
- std::map<std::string,std::string> headers;
- const unsigned int sc = Http::GET(2147483647,ZT_JSONDB_HTTP_TIMEOUT,reinterpret_cast<const struct sockaddr *>(&_httpAddr),_basePath.c_str(),_ZT_JSONDB_GET_HEADERS,headers,body);
- if (sc == 200) {
- try {
- nlohmann::json dbImg(OSUtils::jsonParse(body));
- std::string tmp;
- if (dbImg.is_object()) {
- for(nlohmann::json::iterator i(dbImg.begin());i!=dbImg.end();++i) {
- if (i.value().is_object()) {
- tmp = i.key();
- _db[tmp].obj = i.value();
- }
- }
- return true;
- }
- } catch ( ... ) {} // invalid JSON, so maybe incomplete request
- }
- return false;
- } else {
- std::vector<std::string> dl(OSUtils::listDirectory(p.c_str(),true));
- for(std::vector<std::string>::const_iterator di(dl.begin());di!=dl.end();++di) {
- if ((di->length() > 5)&&(di->substr(di->length() - 5) == ".json")) {
- this->get(b + di->substr(0,di->length() - 5));
- } else {
- this->_reload((p + ZT_PATH_SEPARATOR + *di),(b + *di + ZT_PATH_SEPARATOR));
- }
- }
- return true;
- }
-}
-
-bool JSONDB::_isValidObjectName(const std::string &n)
-{
- if (n.length() == 0)
- return false;
- const char *p = n.c_str();
- char c;
- // For security reasons we should not allow dots, backslashes, or other path characters or potential path characters.
- while ((c = *(p++))) {
- if (!( ((c >= 'a')&&(c <= 'z')) || ((c >= 'A')&&(c <= 'Z')) || ((c >= '0')&&(c <= '9')) || (c == '/') || (c == '_') || (c == '~') || (c == '-') ))
- return false;
- }
- return true;
-}
-
-std::string JSONDB::_genPath(const std::string &n,bool create)
-{
- std::vector<std::string> pt(OSUtils::split(n.c_str(),"/","",""));
- if (pt.size() == 0)
- return std::string();
-
- char sep;
- if (_httpAddr) {
- sep = '/';
- create = false;
- } else {
- sep = ZT_PATH_SEPARATOR;
- }
-
- std::string p(_basePath);
- if (create) OSUtils::mkdir(p.c_str());
- for(unsigned long i=0,j=(unsigned long)(pt.size()-1);i<j;++i) {
- p.push_back(sep);
- p.append(pt[i]);
- if (create) OSUtils::mkdir(p.c_str());
- }
-
- p.push_back(sep);
- p.append(pt[pt.size()-1]);
- p.append(".json");
-
- return p;
-}
-
-} // namespace ZeroTier
diff --git a/controller/JSONDB.hpp b/controller/JSONDB.hpp
deleted file mode 100644
index beafbaf5..00000000
--- a/controller/JSONDB.hpp
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2015 ZeroTier, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef ZT_JSONDB_HPP
-#define ZT_JSONDB_HPP
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <string>
-#include <map>
-#include <stdexcept>
-#include <vector>
-#include <algorithm>
-
-#include "../node/Constants.hpp"
-#include "../node/Utils.hpp"
-#include "../node/InetAddress.hpp"
-#include "../node/Mutex.hpp"
-#include "../ext/json/json.hpp"
-#include "../osdep/OSUtils.hpp"
-#include "../osdep/Http.hpp"
-#include "../osdep/Thread.hpp"
-
-namespace ZeroTier {
-
-/**
- * Hierarchical JSON store that persists into the filesystem or via HTTP
- */
-class JSONDB
-{
-public:
- JSONDB(const std::string &basePath);
-
- bool writeRaw(const std::string &n,const std::string &obj);
-
- bool put(const std::string &n,const nlohmann::json &obj);
-
- inline bool put(const std::string &n1,const std::string &n2,const nlohmann::json &obj) { return this->put((n1 + "/" + n2),obj); }
- inline bool put(const std::string &n1,const std::string &n2,const std::string &n3,const nlohmann::json &obj) { return this->put((n1 + "/" + n2 + "/" + n3),obj); }
- inline bool put(const std::string &n1,const std::string &n2,const std::string &n3,const std::string &n4,const nlohmann::json &obj) { return this->put((n1 + "/" + n2 + "/" + n3 + "/" + n4),obj); }
- inline bool put(const std::string &n1,const std::string &n2,const std::string &n3,const std::string &n4,const std::string &n5,const nlohmann::json &obj) { return this->put((n1 + "/" + n2 + "/" + n3 + "/" + n4 + "/" + n5),obj); }
-
- const nlohmann::json &get(const std::string &n);
-
- inline const nlohmann::json &get(const std::string &n1,const std::string &n2) { return this->get((n1 + "/" + n2)); }
- inline const nlohmann::json &get(const std::string &n1,const std::string &n2,const std::string &n3) { return this->get((n1 + "/" + n2 + "/" + n3)); }
- inline const nlohmann::json &get(const std::string &n1,const std::string &n2,const std::string &n3,const std::string &n4) { return this->get((n1 + "/" + n2 + "/" + n3 + "/" + n4)); }
- inline const nlohmann::json &get(const std::string &n1,const std::string &n2,const std::string &n3,const std::string &n4,const std::string &n5) { return this->get((n1 + "/" + n2 + "/" + n3 + "/" + n4 + "/" + n5)); }
-
- void erase(const std::string &n);
-
- inline void erase(const std::string &n1,const std::string &n2) { this->erase(n1 + "/" + n2); }
- inline void erase(const std::string &n1,const std::string &n2,const std::string &n3) { this->erase(n1 + "/" + n2 + "/" + n3); }
- inline void erase(const std::string &n1,const std::string &n2,const std::string &n3,const std::string &n4) { this->erase(n1 + "/" + n2 + "/" + n3 + "/" + n4); }
- inline void erase(const std::string &n1,const std::string &n2,const std::string &n3,const std::string &n4,const std::string &n5) { this->erase(n1 + "/" + n2 + "/" + n3 + "/" + n4 + "/" + n5); }
-
- template<typename F>
- inline void filter(const std::string &prefix,F func)
- {
- while (!_ready) {
- Thread::sleep(250);
- _ready = _reload(_basePath,std::string());
- }
-
- for(std::map<std::string,_E>::iterator i(_db.lower_bound(prefix));i!=_db.end();) {
- if ((i->first.length() >= prefix.length())&&(!memcmp(i->first.data(),prefix.data(),prefix.length()))) {
- if (!func(i->first,get(i->first))) {
- std::map<std::string,_E>::iterator i2(i); ++i2;
- this->erase(i->first);
- i = i2;
- } else ++i;
- } else break;
- }
- }
-
- inline bool operator==(const JSONDB &db) const { return ((_basePath == db._basePath)&&(_db == db._db)); }
- inline bool operator!=(const JSONDB &db) const { return (!(*this == db)); }
-
-private:
- bool _reload(const std::string &p,const std::string &b);
- bool _isValidObjectName(const std::string &n);
- std::string _genPath(const std::string &n,bool create);
-
- struct _E
- {
- nlohmann::json obj;
- inline bool operator==(const _E &e) const { return (obj == e.obj); }
- inline bool operator!=(const _E &e) const { return (obj != e.obj); }
- };
-
- InetAddress _httpAddr;
- std::string _basePath;
- std::map<std::string,_E> _db;
- volatile bool _ready;
-};
-
-} // namespace ZeroTier
-
-#endif
diff --git a/controller/README.md b/controller/README.md
index 8a1a3dff..23bd931d 100644
--- a/controller/README.md
+++ b/controller/README.md
@@ -1,43 +1,47 @@
Network Controller Microservice
======
-Every ZeroTier virtual network has a *network controller*. This is our reference implementation and is the same one we use to power our own hosted services at [my.zerotier.com](https://my.zerotier.com/). Network controllers act as configuration servers and certificate authorities for the members of networks. Controllers are located on the network by simply parsing out the first 10 digits of a network's 16-digit network ID: these are the address of the controller.
+Every ZeroTier virtual network has a *network controller* responsible for admitting members to the network, issuing certificates, and issuing default configuration information.
-As of ZeroTier One version 1.2.0 this code is included in normal builds for desktop, laptop, and server (Linux, etc.) targets, allowing any device to create virtual networks without having to be rebuilt from source with special flags to enable this feature. While this does offer a convenient way to create ad-hoc networks or experiment, we recommend running a dedicated controller somewhere secure and stable for any "serious" use case.
+This is our reference controller implementation and is the same one we use to power our own hosted services at [my.zerotier.com](https://my.zerotier.com/). As of ZeroTier One version 1.2.0 this code is included in normal builds for desktop, laptop, and server (Linux, etc.) targets.
Controller data is stored in JSON format under `controller.d` in the ZeroTier working directory. It can be copied, rsync'd, placed in `git`, etc. The files under `controller.d` should not be modified in place while the controller is running or data loss may result, and if they are edited directly take care not to save corrupt JSON since that can also lead to data loss when the controller is restarted. Going through the API is strongly preferred to directly modifying these files.
-### Upgrading from Older (1.1.14 or earlier) Versions
-
-Older versions of this code used a SQLite database instead of in-filesystem JSON. A migration utility called `migrate-sqlite` is included here and *must* be used to migrate this data to the new format. If the controller is started with an old `controller.db` in its working directory it will terminate after printing an error to *stderr*. This is done to prevent "surprises" for those running DIY controllers using the old code.
-
-The migration tool is written in nodeJS and can be used like this:
-
- cd migrate-sqlite
- npm install
- node migrate.js </path/to/controller.db> </path/to/controller.d>
-
-Very old versions of nodeJS may have issues. We tested it with version 7.
+See the API section below for information about controlling the controller.
### Scalability and Reliability
Controllers can in theory host up to 2^24 networks and serve many millions of devices (or more), but we recommend spreading large numbers of networks across many controllers for load balancing and fault tolerance reasons. Since the controller uses the filesystem as its data store we recommend fast filesystems and fast SSD drives for heavily loaded controllers.
-Since ZeroTier nodes are mobile and do not need static IPs, implementing high availability fail-over for controllers is easy. Just replicate their working directories from master to backup and have something automatically fire up the backup if the master goes down. Many modern orchestration tools have built-in support for this. It would also be possible in theory to run controllers on a replicated or distributed filesystem, but we haven't tested this yet.
+Since ZeroTier nodes are mobile and do not need static IPs, implementing high availability fail-over for controllers is easy. Just replicate their working directories from master to backup and have something automatically fire up the backup if the master goes down. Modern orchestration tools like Nomad and Kubernetes can be of help here.
### Dockerizing Controllers
ZeroTier network controllers can easily be run in Docker or other container systems. Since containers do not need to actually join networks, extra privilege options like "--device=/dev/net/tun --privileged" are not needed. You'll just need to map the local JSON API port of the running controller and allow it to access the Internet (over UDP/9993 at a minimum) so things can reach and query it.
+### RethinkDB Database Implementation
+
+The default controller stores its data in the filesystem in `controller.d` under ZeroTier's home folder. There's an alternative implementation that stores data in RethinkDB that can be built with `make central-controller`. Right now this is only guaranteed to build and run on Linux and is designed for use with [ZeroTier Central](https://my.zerotier.com/). You're welcome to use it but we don't "officially" support it for end-user use and it could change at any time.
+
+### Upgrading from Older (1.1.14 or earlier) Versions
+
+Older versions of this code used a SQLite database instead of in-filesystem JSON. A migration utility called `migrate-sqlite` is included here and *must* be used to migrate this data to the new format. If the controller is started with an old `controller.db` in its working directory it will terminate after printing an error to *stderr*. This is done to prevent "surprises" for those running DIY controllers using the old code.
+
+The migration tool is written in nodeJS and can be used like this:
+
+ cd migrate-sqlite
+ npm install
+ node migrate.js </path/to/controller.db> </path/to/controller.d>
+
### Network Controller API
The controller API is hosted via the same JSON API endpoint that ZeroTier One uses for local control (usually at 127.0.0.1 port 9993). All controller options are routed under the `/controller` base path.
-The controller microservice does not implement any fine-grained access control (authentication is via authtoken.secret, simply append the value from authtoken.secret file, into a new querystring parameter named "auth" - for example `/controller/network?auth=6hdmozf8k5ds39kabcdefabc`) or other complex mangement features. It just takes network and network member configurations and reponds to controller queries. We have an enterprise product called [ZeroTier Central](https://my.zerotier.com/) that we host as a service (and that companies can license to self-host) that does this.
+The controller microservice itself does not implement any fine-grained access control. Access control is via the ZeroTier control interface itself and `authtoken.secret`. This can be sent as the `X-ZT1-Auth` HTTP header field or appended to the URL as `?auth=<token>`. Take care when doing the latter that request URLs are not being logged.
-All working network IDs on a controller must begin with the controller's ZeroTier address. The API will *allow* "foreign" networks to be added but the controller will have no way of doing anything with them since nobody will know to query it. (In the future we might support secondaries, which would make this relevant.)
+While networks with any valid ID can be added to the controller's database, it will only actually work to control networks whose first 10 hex digits correspond with the network controller's ZeroTier ID. See [section 2.2.1 of the ZeroTier manual](https://zerotier.com/manual.shtml#2_2_1).
-The JSON API is *very* sensitive about types. Integers must be integers and strings strings, etc. Incorrectly typed and unrecognized fields may result in ignored fields or a 400 (bad request) error.
+The controller JSON API is *very* sensitive about types. Integers must be integers and strings strings, etc. Incorrect types may be ignored, set to default values, or set to undefined values.
#### `/controller`
@@ -78,34 +82,30 @@ Example:
| Field | Type | Description | Writable |
| --------------------- | ------------- | ------------------------------------------------- | -------- |
| id | string | 16-digit network ID | no |
-| nwid | string | 16-digit network ID (old, but still around) | no |
-| clock | integer | Current clock, ms since epoch | no |
+| nwid | string | 16-digit network ID (legacy) | no |
+| objtype | string | Always "network" | no |
| name | string | A short name for this network | YES |
+| creationTime | integer | Time network record was created (ms since epoch) | no |
| private | boolean | Is access control enabled? | YES |
| enableBroadcast | boolean | Ethernet ff:ff:ff:ff:ff:ff allowed? | YES |
-| allowPassiveBridging | boolean | Allow any member to bridge (very experimental) | YES |
| v4AssignMode | object | IPv4 management and assign options (see below) | YES |
| v6AssignMode | object | IPv6 management and assign options (see below) | YES |
+| mtu | integer | Network MTU (default: 2800) | YES |
| multicastLimit | integer | Maximum recipients for a multicast packet | YES |
| creationTime | integer | Time network was first created | no |
| revision | integer | Network config revision counter | no |
-| authorizedMemberCount | integer | Number of authorized members (for private nets) | no |
-| activeMemberCount | integer | Number of members that appear to be online | no |
-| totalMemberCount | integer | Total known members of this network | no |
| routes | array[object] | Managed IPv4 and IPv6 routes; see below | YES |
| ipAssignmentPools | array[object] | IP auto-assign ranges; see below | YES |
| rules | array[object] | Traffic rules; see below | YES |
-
-Recent changes:
-
- * The `ipLocalRoutes` field appeared in older versions but is no longer present. Routes will now show up in `routes`.
- * The `relays` field is gone since network preferred relays are gone. This capability is replaced by VL1 level federation ("federated roots").
-
-Other important points:
+| capabilities | array[object] | Array of capability objects (see below) | YES |
+| tags | array[object] | Array of tag objects (see below) | YES |
+| remoteTraceTarget | string | 10-digit ZeroTier ID of remote trace target | YES |
+| remoteTraceLevel | integer | Remote trace verbosity level | YES |
* Networks without rules won't carry any traffic. If you don't specify any on network creation an "accept anything" rule set will automatically be added.
* Managed IP address assignments and IP assignment pools that do not fall within a route configured in `routes` are ignored and won't be used or sent to members.
* The default for `private` is `true` and this is probably what you want. Turning `private` off means *anyone* can join your network with only its 16-digit network ID. It's also impossible to de-authorize a member as these networks don't issue or enforce certificates. Such "party line" networks are used for decentralized app backplanes, gaming, and testing but are otherwise not common.
+ * Changing the MTU can be disruptive and on some operating systems may require a leave/rejoin of the network or a restart of the ZeroTier service.
**Auto-Assign Modes:**
@@ -191,7 +191,7 @@ The entry types and their additional fields are:
| `MATCH_TAGS_SAMENESS` | Match if both sides' tags differ by no more than value | `id`,`value` |
| `MATCH_TAGS_BITWISE_AND` | Match if both sides' tags AND to value | `id`,`value` |
| `MATCH_TAGS_BITWISE_OR` | Match if both sides' tags OR to value | `id`,`value` |
-| `MATCH_TAGS_BITWISE_XOR` | Match if both sides` tags XOR to value | `id`,`value` |
+| `MATCH_TAGS_BITWISE_XOR` | Match if both sides' tags XOR to value | `id`,`value` |
Important notes about rules engine behavior:
@@ -227,28 +227,15 @@ This returns an object containing all currently online members and the most rece
| id | string | Member's 10-digit ZeroTier address | no |
| address | string | Member's 10-digit ZeroTier address | no |
| nwid | string | 16-digit network ID | no |
-| clock | integer | Current clock, ms since epoch | no |
| authorized | boolean | Is member authorized? (for private networks) | YES |
-| authHistory | array[object] | History of auth changes, latest at end | no |
| activeBridge | boolean | Member is able to bridge to other Ethernet nets | YES |
| identity | string | Member's public ZeroTier identity (if known) | no |
| ipAssignments | array[string] | Managed IP address assignments | YES |
-| memberRevision | integer | Member revision counter | no |
-| recentLog | array[object] | Recent member activity log; see below | no |
+| revision | integer | Member revision counter | no |
+| vMajor | integer | Most recently known major version | no |
+| vMinor | integer | Most recently known minor version | no |
+| vRev | integer | Most recently known revision | no |
+| vProto | integer | Most recently known protocl version | no |
Note that managed IP assignments are only used if they fall within a managed route. Otherwise they are ignored.
-**Recent log object format:**
-
-| Field | Type | Description |
-| --------------------- | ------------- | ------------------------------------------------- |
-| ts | integer | Time of request, ms since epoch |
-| auth | boolean | Was member authorized? |
-| authBy | string | How was member authorized? |
-| vMajor | integer | Client major version or -1 if unknown |
-| vMinor | integer | Client minor version or -1 if unknown |
-| vRev | integer | Client revision or -1 if unknown |
-| vProto | integer | ZeroTier protocol version reported by client |
-| fromAddr | string | Physical address if known |
-
-The controller can only know a member's `fromAddr` if it's able to establish a direct path to it. Members behind very restrictive firewalls may not have this information since the controller will be receiving the member's requests by way of a relay. ZeroTier does not back-trace IP paths as packets are relayed since this would add a lot of protocol overhead.
diff --git a/controller/RethinkDB.cpp b/controller/RethinkDB.cpp
new file mode 100644
index 00000000..f6c8a59c
--- /dev/null
+++ b/controller/RethinkDB.cpp
@@ -0,0 +1,488 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//#define ZT_CONTROLLER_USE_RETHINKDB
+
+#ifdef ZT_CONTROLLER_USE_RETHINKDB
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "RethinkDB.hpp"
+#include "EmbeddedNetworkController.hpp"
+
+#include "../version.h"
+
+#include <chrono>
+#include <algorithm>
+#include <stdexcept>
+
+#include "../ext/librethinkdbxx/build/include/rethinkdb.h"
+
+namespace R = RethinkDB;
+using json = nlohmann::json;
+
+namespace ZeroTier {
+
+static const char *_timestr()
+{
+ time_t t = time(0);
+ char *ts = ctime(&t);
+ char *p = ts;
+ if (!p)
+ return "";
+ while (*p) {
+ if (*p == '\n') {
+ *p = (char)0;
+ break;
+ }
+ ++p;
+ }
+ return ts;
+}
+
+RethinkDB::RethinkDB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path) :
+ DB(nc,myId,path),
+ _ready(2), // two tables need to be synchronized before we're ready, so this is ready when it reaches 0
+ _run(1),
+ _waitNoticePrinted(false)
+{
+ // rethinkdb:host:port:db[:auth]
+ std::vector<std::string> ps(OSUtils::split(path,":","",""));
+ if ((ps.size() < 4)||(ps[0] != "rethinkdb"))
+ throw std::runtime_error("invalid rethinkdb database url");
+ _host = ps[1];
+ _port = Utils::strToInt(ps[2].c_str());
+ _db = ps[3];
+ if (ps.size() > 4)
+ _auth = ps[4];
+
+ _readyLock.lock();
+
+ _membersDbWatcher = std::thread([this]() {
+ try {
+ while (_run == 1) {
+ try {
+ std::unique_ptr<R::Connection> rdb(R::connect(this->_host,this->_port,this->_auth));
+ if (rdb) {
+ _membersDbWatcherConnection = (void *)rdb.get();
+ auto cur = R::db(this->_db).table("Member",R::optargs("read_mode","outdated")).get_all(this->_myAddressStr,R::optargs("index","controllerId")).changes(R::optargs("squash",0.05,"include_initial",true,"include_types",true,"include_states",true)).run(*rdb);
+ while (cur.has_next()) {
+ if (_run != 1) break;
+ json tmp(json::parse(cur.next().as_json()));
+ if ((tmp["type"] == "state")&&(tmp["state"] == "ready")) {
+ if (--this->_ready == 0) {
+ if (_waitNoticePrinted)
+ fprintf(stderr,"[%s] NOTICE: %.10llx controller RethinkDB data download complete." ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
+ this->_readyLock.unlock();
+ }
+ } else {
+ try {
+ json &ov = tmp["old_val"];
+ json &nv = tmp["new_val"];
+ json oldConfig,newConfig;
+ if (ov.is_object()) oldConfig = ov["config"];
+ if (nv.is_object()) newConfig = nv["config"];
+ if (oldConfig.is_object()||newConfig.is_object())
+ this->_memberChanged(oldConfig,newConfig,(this->_ready <= 0));
+ } catch ( ... ) {} // ignore bad records
+ }
+ }
+ }
+ } catch (std::exception &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (member change stream): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.what());
+ } catch (R::Error &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (member change stream): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.message.c_str());
+ } catch ( ... ) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (member change stream): unknown exception" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(250));
+ }
+ } catch ( ... ) {}
+ });
+
+ _networksDbWatcher = std::thread([this]() {
+ try {
+ while (_run == 1) {
+ try {
+ std::unique_ptr<R::Connection> rdb(R::connect(this->_host,this->_port,this->_auth));
+ if (rdb) {
+ _networksDbWatcherConnection = (void *)rdb.get();
+ auto cur = R::db(this->_db).table("Network",R::optargs("read_mode","outdated")).get_all(this->_myAddressStr,R::optargs("index","controllerId")).changes(R::optargs("squash",0.05,"include_initial",true,"include_types",true,"include_states",true)).run(*rdb);
+ while (cur.has_next()) {
+ if (_run != 1) break;
+ json tmp(json::parse(cur.next().as_json()));
+ if ((tmp["type"] == "state")&&(tmp["state"] == "ready")) {
+ if (--this->_ready == 0) {
+ if (_waitNoticePrinted)
+ fprintf(stderr,"[%s] NOTICE: %.10llx controller RethinkDB data download complete." ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
+ this->_readyLock.unlock();
+ }
+ } else {
+ try {
+ json &ov = tmp["old_val"];
+ json &nv = tmp["new_val"];
+ json oldConfig,newConfig;
+ if (ov.is_object()) oldConfig = ov["config"];
+ if (nv.is_object()) newConfig = nv["config"];
+ if (oldConfig.is_object()||newConfig.is_object())
+ this->_networkChanged(oldConfig,newConfig,(this->_ready <= 0));
+ } catch ( ... ) {} // ignore bad records
+ }
+ }
+ }
+ } catch (std::exception &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (network change stream): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.what());
+ } catch (R::Error &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (network change stream): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.message.c_str());
+ } catch ( ... ) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (network change stream): unknown exception" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(250));
+ }
+ } catch ( ... ) {}
+ });
+
+ for(int t=0;t<ZT_CONTROLLER_RETHINKDB_COMMIT_THREADS;++t) {
+ _commitThread[t] = std::thread([this]() {
+ try {
+ std::unique_ptr<R::Connection> rdb;
+ nlohmann::json *config = (nlohmann::json *)0;
+ while ((this->_commitQueue.get(config))&&(_run == 1)) {
+ if (!config)
+ continue;
+ nlohmann::json record;
+ const char *table = (const char *)0;
+ std::string deleteId;
+ try {
+ const std::string objtype = (*config)["objtype"];
+ if (objtype == "member") {
+ const std::string nwid = (*config)["nwid"];
+ const std::string id = (*config)["id"];
+ record["id"] = nwid + "-" + id;
+ record["controllerId"] = this->_myAddressStr;
+ record["networkId"] = nwid;
+ record["nodeId"] = id;
+ record["config"] = *config;
+ table = "Member";
+ } else if (objtype == "network") {
+ const std::string id = (*config)["id"];
+ record["id"] = id;
+ record["controllerId"] = this->_myAddressStr;
+ record["config"] = *config;
+ table = "Network";
+ } else if (objtype == "trace") {
+ record = *config;
+ table = "RemoteTrace";
+ } else if (objtype == "_delete_network") {
+ deleteId = (*config)["id"];
+ table = "Network";
+ } else if (objtype == "_delete_member") {
+ deleteId = (*config)["nwid"];
+ deleteId.push_back('-');
+ const std::string tmp = (*config)["id"];
+ deleteId.append(tmp);
+ table = "Member";
+ }
+ } catch (std::exception &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update record creation): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.what());
+ table = (const char *)0;
+ } catch (R::Error &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update record creation): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.message.c_str());
+ table = (const char *)0;
+ } catch ( ... ) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update record creation): unknown exception" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
+ table = (const char *)0;
+ }
+ delete config;
+ if (!table)
+ continue;
+ const std::string jdump(OSUtils::jsonDump(record,-1));
+
+ while (_run == 1) {
+ try {
+ if (!rdb)
+ rdb = R::connect(this->_host,this->_port,this->_auth);
+ if (rdb) {
+ if (deleteId.length() > 0) {
+ //printf("DELETE: %s" ZT_EOL_S,deleteId.c_str());
+ R::db(this->_db).table(table).get(deleteId).delete_().run(*rdb);
+ } else {
+ //printf("UPSERT: %s" ZT_EOL_S,record.dump().c_str());
+ R::db(this->_db).table(table).insert(R::Datum::from_json(jdump),R::optargs("conflict","update","return_changes",false)).run(*rdb);
+ }
+ break;
+ } else {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): connect failed (will retry)" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
+ rdb.reset();
+ }
+ } catch (std::exception &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): %s [%s]" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.what(),jdump.c_str());
+ rdb.reset();
+ } catch (R::Error &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): %s [%s]" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.message.c_str(),jdump.c_str());
+ rdb.reset();
+ } catch ( ... ) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): unknown exception [%s]" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),jdump.c_str());
+ rdb.reset();
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(250));
+ }
+ }
+ } catch (std::exception &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update outer loop): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.what());
+ } catch (R::Error &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update outer loop): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.message.c_str());
+ } catch ( ... ) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update outer loop): unknown exception" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
+ }
+ });
+ }
+
+ _onlineNotificationThread = std::thread([this]() {
+ int64_t lastUpdatedNetworkStatus = 0;
+ std::unordered_map< std::pair<uint64_t,uint64_t>,int64_t,_PairHasher > lastOnlineCumulative;
+ try {
+ std::unique_ptr<R::Connection> rdb;
+ while (_run == 1) {
+ try {
+ if (!rdb)
+ rdb = R::connect(this->_host,this->_port,this->_auth);
+ if (rdb) {
+ R::Array batch;
+ R::Object tmpobj;
+
+ std::unordered_map< std::pair<uint64_t,uint64_t>,std::pair<int64_t,InetAddress>,_PairHasher > lastOnline;
+ {
+ std::lock_guard<std::mutex> l(_lastOnline_l);
+ lastOnline.swap(_lastOnline);
+ }
+
+ for(auto i=lastOnline.begin();i!=lastOnline.end();++i) {
+ lastOnlineCumulative[i->first] = i->second.first;
+ char tmp[64],tmp2[64];
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx-%.10llx",i->first.first,i->first.second);
+ tmpobj["id"] = tmp;
+ tmpobj["ts"] = i->second.first;
+ tmpobj["phy"] = i->second.second.toIpString(tmp2);
+ batch.emplace_back(tmpobj);
+ if (batch.size() >= 1024) {
+ R::db(this->_db).table("MemberStatus",R::optargs("read_mode","outdated")).insert(batch,R::optargs("conflict","update")).run(*rdb);
+ batch.clear();
+ }
+ }
+ if (batch.size() > 0) {
+ R::db(this->_db).table("MemberStatus",R::optargs("read_mode","outdated")).insert(batch,R::optargs("conflict","update")).run(*rdb);
+ batch.clear();
+ }
+ tmpobj.clear();
+
+ const int64_t now = OSUtils::now();
+ if ((now - lastUpdatedNetworkStatus) > 10000) {
+ lastUpdatedNetworkStatus = now;
+
+ std::vector< std::pair< uint64_t,std::shared_ptr<_Network> > > networks;
+ {
+ std::lock_guard<std::mutex> l(_networks_l);
+ networks.reserve(_networks.size() + 1);
+ for(auto i=_networks.begin();i!=_networks.end();++i)
+ networks.push_back(*i);
+ }
+
+ for(auto i=networks.begin();i!=networks.end();++i) {
+ char tmp[64];
+ Utils::hex(i->first,tmp);
+ tmpobj["id"] = tmp;
+ {
+ std::lock_guard<std::mutex> l2(i->second->lock);
+ tmpobj["authorizedMemberCount"] = i->second->authorizedMembers.size();
+ tmpobj["totalMemberCount"] = i->second->members.size();
+ unsigned long onlineMemberCount = 0;
+ for(auto m=i->second->members.begin();m!=i->second->members.end();++m) {
+ auto lo = lastOnlineCumulative.find(std::pair<uint64_t,uint64_t>(i->first,m->first));
+ if (lo != lastOnlineCumulative.end()) {
+ if ((now - lo->second) <= (ZT_NETWORK_AUTOCONF_DELAY * 2))
+ ++onlineMemberCount;
+ else lastOnlineCumulative.erase(lo);
+ }
+ }
+ tmpobj["onlineMemberCount"] = onlineMemberCount;
+ tmpobj["bridgeCount"] = i->second->activeBridgeMembers.size();
+ tmpobj["ts"] = now;
+ }
+ batch.emplace_back(tmpobj);
+ if (batch.size() >= 1024) {
+ R::db(this->_db).table("NetworkStatus",R::optargs("read_mode","outdated")).insert(batch,R::optargs("conflict","update")).run(*rdb);
+ batch.clear();
+ }
+ }
+ if (batch.size() > 0) {
+ R::db(this->_db).table("NetworkStatus",R::optargs("read_mode","outdated")).insert(batch,R::optargs("conflict","update")).run(*rdb);
+ batch.clear();
+ }
+ }
+ }
+ } catch (std::exception &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (node status update): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.what());
+ rdb.reset();
+ } catch (R::Error &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (node status update): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.message.c_str());
+ rdb.reset();
+ } catch ( ... ) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (node status update): unknown exception" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
+ rdb.reset();
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(250));
+ }
+ } catch ( ... ) {}
+ });
+
+ _heartbeatThread = std::thread([this]() {
+ try {
+ R::Object controllerRecord;
+ std::unique_ptr<R::Connection> rdb;
+
+ {
+ char publicId[1024];
+ //char secretId[1024];
+ char hostname[1024];
+ this->_myId.toString(false,publicId);
+ //this->_myId.toString(true,secretId);
+ if (gethostname(hostname,sizeof(hostname)) != 0) {
+ hostname[0] = (char)0;
+ } else {
+ for(int i=0;i<sizeof(hostname);++i) {
+ if ((hostname[i] == '.')||(hostname[i] == 0)) {
+ hostname[i] = (char)0;
+ break;
+ }
+ }
+ }
+ controllerRecord["id"] = this->_myAddressStr.c_str();
+ controllerRecord["publicIdentity"] = publicId;
+ //controllerRecord["secretIdentity"] = secretId;
+ if (hostname[0])
+ controllerRecord["clusterHost"] = hostname;
+ controllerRecord["vMajor"] = ZEROTIER_ONE_VERSION_MAJOR;
+ controllerRecord["vMinor"] = ZEROTIER_ONE_VERSION_MINOR;
+ controllerRecord["vRev"] = ZEROTIER_ONE_VERSION_REVISION;
+ controllerRecord["vBuild"] = ZEROTIER_ONE_VERSION_BUILD;
+ }
+
+ while (_run == 1) {
+ try {
+ if (!rdb)
+ rdb = R::connect(this->_host,this->_port,this->_auth);
+ if (rdb) {
+ controllerRecord["lastAlive"] = OSUtils::now();
+ //printf("HEARTBEAT: %s" ZT_EOL_S,tmp);
+ R::db(this->_db).table("Controller",R::optargs("read_mode","outdated")).insert(controllerRecord,R::optargs("conflict","update")).run(*rdb);
+ }
+ } catch ( ... ) {
+ rdb.reset();
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+ }
+ } catch ( ... ) {}
+ });
+}
+
+RethinkDB::~RethinkDB()
+{
+ _run = 0;
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ _commitQueue.stop();
+ for(int t=0;t<ZT_CONTROLLER_RETHINKDB_COMMIT_THREADS;++t)
+ _commitThread[t].join();
+ if (_membersDbWatcherConnection)
+ ((R::Connection *)_membersDbWatcherConnection)->close();
+ if (_networksDbWatcherConnection)
+ ((R::Connection *)_networksDbWatcherConnection)->close();
+ _membersDbWatcher.join();
+ _networksDbWatcher.join();
+ _heartbeatThread.join();
+ _onlineNotificationThread.join();
+}
+
+bool RethinkDB::waitForReady()
+{
+ while (_ready > 0) {
+ if (!_waitNoticePrinted) {
+ _waitNoticePrinted = true;
+ fprintf(stderr,"[%s] NOTICE: %.10llx controller RethinkDB waiting for initial data download..." ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
+ }
+ _readyLock.lock();
+ _readyLock.unlock();
+ }
+ return true;
+}
+
+void RethinkDB::save(nlohmann::json *orig,nlohmann::json &record)
+{
+ if (!record.is_object()) // sanity check
+ return;
+ waitForReady();
+ if (orig) {
+ if (*orig != record) {
+ record["revision"] = OSUtils::jsonInt(record["revision"],0ULL) + 1;
+ _commitQueue.post(new nlohmann::json(record));
+ }
+ } else {
+ record["revision"] = 1;
+ _commitQueue.post(new nlohmann::json(record));
+ }
+}
+
+void RethinkDB::eraseNetwork(const uint64_t networkId)
+{
+ char tmp2[24];
+ waitForReady();
+ Utils::hex(networkId,tmp2);
+ json *tmp = new json();
+ (*tmp)["id"] = tmp2;
+ (*tmp)["objtype"] = "_delete_network"; // pseudo-type, tells thread to delete network
+ _commitQueue.post(tmp);
+}
+
+void RethinkDB::eraseMember(const uint64_t networkId,const uint64_t memberId)
+{
+ char tmp2[24];
+ json *tmp = new json();
+ waitForReady();
+ Utils::hex(networkId,tmp2);
+ (*tmp)["nwid"] = tmp2;
+ Utils::hex10(memberId,tmp2);
+ (*tmp)["id"] = tmp2;
+ (*tmp)["objtype"] = "_delete_member"; // pseudo-type, tells thread to delete network
+ _commitQueue.post(tmp);
+}
+
+void RethinkDB::nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress)
+{
+ std::lock_guard<std::mutex> l(_lastOnline_l);
+ std::pair<int64_t,InetAddress> &i = _lastOnline[std::pair<uint64_t,uint64_t>(networkId,memberId)];
+ i.first = OSUtils::now();
+ if (physicalAddress)
+ i.second = physicalAddress;
+}
+
+} // namespace ZeroTier
+
+#endif // ZT_CONTROLLER_USE_RETHINKDB
diff --git a/controller/RethinkDB.hpp b/controller/RethinkDB.hpp
new file mode 100644
index 00000000..b1049ac3
--- /dev/null
+++ b/controller/RethinkDB.hpp
@@ -0,0 +1,84 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef ZT_CONTROLLER_USE_RETHINKDB
+
+#ifndef ZT_CONTROLLER_RETHINKDB_HPP
+#define ZT_CONTROLLER_RETHINKDB_HPP
+
+#include "DB.hpp"
+
+#define ZT_CONTROLLER_RETHINKDB_COMMIT_THREADS 4
+
+namespace ZeroTier
+{
+
+/**
+ * A controller database driver that talks to RethinkDB
+ *
+ * This is for use with ZeroTier Central. Others are free to build and use it
+ * but be aware that we might change it at any time.
+ */
+class RethinkDB : public DB
+{
+public:
+ RethinkDB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path);
+ virtual ~RethinkDB();
+
+ virtual bool waitForReady();
+ virtual void save(nlohmann::json *orig,nlohmann::json &record);
+ virtual void eraseNetwork(const uint64_t networkId);
+ virtual void eraseMember(const uint64_t networkId,const uint64_t memberId);
+ virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress);
+
+protected:
+ struct _PairHasher
+ {
+ inline std::size_t operator()(const std::pair<uint64_t,uint64_t> &p) const { return (std::size_t)(p.first ^ p.second); }
+ };
+
+ std::string _host;
+ std::string _db;
+ std::string _auth;
+ int _port;
+
+ void *_networksDbWatcherConnection;
+ void *_membersDbWatcherConnection;
+ std::thread _networksDbWatcher;
+ std::thread _membersDbWatcher;
+
+ BlockingQueue< nlohmann::json * > _commitQueue;
+ std::thread _commitThread[ZT_CONTROLLER_RETHINKDB_COMMIT_THREADS];
+
+ std::unordered_map< std::pair<uint64_t,uint64_t>,std::pair<int64_t,InetAddress>,_PairHasher > _lastOnline;
+ mutable std::mutex _lastOnline_l;
+ std::thread _onlineNotificationThread;
+
+ std::thread _heartbeatThread;
+
+ mutable std::mutex _readyLock; // locked until ready
+ std::atomic<int> _ready;
+ std::atomic<int> _run;
+ mutable volatile bool _waitNoticePrinted;
+};
+
+} // namespace ZeroTier
+
+#endif
+
+#endif // ZT_CONTROLLER_USE_RETHINKDB
diff --git a/controller/migrate-sqlite/migrate.js b/controller/migrate-sqlite/migrate.js
deleted file mode 100644
index ac9678a7..00000000
--- a/controller/migrate-sqlite/migrate.js
+++ /dev/null
@@ -1,320 +0,0 @@
-'use strict';
-
-var sqlite3 = require('sqlite3').verbose();
-var fs = require('fs');
-var async = require('async');
-
-function blobToIPv4(b)
-{
- if (!b)
- return null;
- if (b.length !== 16)
- return null;
- return b.readUInt8(12).toString()+'.'+b.readUInt8(13).toString()+'.'+b.readUInt8(14).toString()+'.'+b.readUInt8(15).toString();
-}
-function blobToIPv6(b)
-{
- if (!b)
- return null;
- if (b.length !== 16)
- return null;
- var s = '';
- for(var i=0;i<16;++i) {
- var x = b.readUInt8(i).toString(16);
- if (x.length === 1)
- s += '0';
- s += x;
- if ((((i+1) & 1) === 0)&&(i !== 15))
- s += ':';
- }
- return s;
-}
-
-if (process.argv.length !== 4) {
- console.log('ZeroTier Old Sqlite3 Controller DB Migration Utility');
- console.log('(c)2017 ZeroTier, Inc. [GPL3]');
- console.log('');
- console.log('Usage: node migrate.js </path/to/controller.db> </path/to/controller.d>');
- console.log('');
- console.log('The first argument must be the path to the old Sqlite3 controller.db');
- console.log('file. The second must be the path to the EMPTY controller.d database');
- console.log('directory for a new (1.1.17 or newer) controller. If this path does');
- console.log('not exist it will be created.');
- console.log('');
- console.log('WARNING: this will ONLY work correctly on a 1.1.14 controller database.');
- console.log('If your controller is old you should first upgrade to 1.1.14 and run the');
- console.log('controller so that it will brings its Sqlite3 database up to the latest');
- console.log('version before running this migration.');
- console.log('');
- process.exit(1);
-}
-
-var oldDbPath = process.argv[2];
-var newDbPath = process.argv[3];
-
-console.log('Starting migrate of "'+oldDbPath+'" to "'+newDbPath+'"...');
-console.log('');
-
-var old = new sqlite3.Database(oldDbPath);
-
-var networks = {};
-
-var nodeIdentities = {};
-var networkCount = 0;
-var memberCount = 0;
-var routeCount = 0;
-var ipAssignmentPoolCount = 0;
-var ipAssignmentCount = 0;
-var ruleCount = 0;
-var oldSchemaVersion = -1;
-
-async.series([function(nextStep) {
-
- old.each('SELECT v from Config WHERE k = \'schemaVersion\'',function(err,row) {
- oldSchemaVersion = parseInt(row.v)||-1;
- },nextStep);
-
-},function(nextStep) {
-
- if (oldSchemaVersion !== 4) {
- console.log('FATAL: this MUST be run on a 1.1.14 controller.db! Upgrade your old');
- console.log('controller to 1.1.14 first and run it once to bring its DB up to date.');
- return process.exit(1);
- }
-
- console.log('Reading networks...');
- old.each('SELECT * FROM Network',function(err,row) {
- if ((typeof row.id === 'string')&&(row.id.length === 16)) {
- var flags = parseInt(row.flags)||0;
- networks[row.id] = {
- id: row.id,
- nwid: row.id,
- objtype: 'network',
- authTokens: [],
- capabilities: [],
- creationTime: parseInt(row.creationTime)||0,
- enableBroadcast: !!row.enableBroadcast,
- ipAssignmentPools: [],
- lastModified: Date.now(),
- multicastLimit: row.multicastLimit||32,
- name: row.name||'',
- private: !!row.private,
- revision: parseInt(row.revision)||1,
- rules: [{ 'type': 'ACTION_ACCEPT' }], // populated later if there are defined rules, otherwise default is allow all
- routes: [],
- v4AssignMode: {
- 'zt': ((flags & 1) !== 0)
- },
- v6AssignMode: {
- '6plane': ((flags & 4) !== 0),
- 'rfc4193': ((flags & 2) !== 0),
- 'zt': ((flags & 8) !== 0)
- },
- _members: {} // temporary
- };
- ++networkCount;
- //console.log(networks[row.id]);
- }
- },nextStep);
-
-},function(nextStep) {
-
- console.log(' '+networkCount+' networks.');
- console.log('Reading network route definitions...');
- old.each('SELECT * from Route WHERE ipVersion = 4 OR ipVersion = 6',function(err,row) {
- var network = networks[row.networkId];
- if (network) {
- var rt = {
- target: (((row.ipVersion == 4) ? blobToIPv4(row.target) : blobToIPv6(row.target))+'/'+row.targetNetmaskBits),
- via: ((row.via) ? ((row.ipVersion == 4) ? blobToIPv4(row.via) : blobToIPv6(row.via)) : null)
- };
- network.routes.push(rt);
- ++routeCount;
- }
- },nextStep);
-
-},function(nextStep) {
-
- console.log(' '+routeCount+' routes in '+networkCount+' networks.');
- console.log('Reading IP assignment pools...');
- old.each('SELECT * FROM IpAssignmentPool WHERE ipVersion = 4 OR ipVersion = 6',function(err,row) {
- var network = networks[row.networkId];
- if (network) {
- var p = {
- ipRangeStart: ((row.ipVersion == 4) ? blobToIPv4(row.ipRangeStart) : blobToIPv6(row.ipRangeStart)),
- ipRangeEnd: ((row.ipVersion == 4) ? blobToIPv4(row.ipRangeEnd) : blobToIPv6(row.ipRangeEnd))
- };
- network.ipAssignmentPools.push(p);
- ++ipAssignmentPoolCount;
- }
- },nextStep);
-
-},function(nextStep) {
-
- console.log(' '+ipAssignmentPoolCount+' IP assignment pools in '+networkCount+' networks.');
- console.log('Reading known node identities...');
- old.each('SELECT * FROM Node',function(err,row) {
- nodeIdentities[row.id] = row.identity;
- },nextStep);
-
-},function(nextStep) {
-
- console.log(' '+Object.keys(nodeIdentities).length+' known identities.');
- console.log('Reading network members...');
- old.each('SELECT * FROM Member',function(err,row) {
- var network = networks[row.networkId];
- if (network) {
- network._members[row.nodeId] = {
- id: row.nodeId,
- address: row.nodeId,
- objtype: 'member',
- authorized: !!row.authorized,
- activeBridge: !!row.activeBridge,
- authHistory: [],
- capabilities: [],
- creationTime: 0,
- identity: nodeIdentities[row.nodeId]||null,
- ipAssignments: [],
- lastAuthorizedTime: (row.authorized) ? Date.now() : 0,
- lastDeauthorizedTime: (row.authorized) ? 0 : Date.now(),
- lastModified: Date.now(),
- lastRequestMetaData: '',
- noAutoAssignIps: false,
- nwid: row.networkId,
- revision: parseInt(row.memberRevision)||1,
- tags: [],
- recentLog: []
- };
- ++memberCount;
- //console.log(network._members[row.nodeId]);
- }
- },nextStep);
-
-},function(nextStep) {
-
- console.log(' '+memberCount+' members of '+networkCount+' networks.');
- console.log('Reading static IP assignments...');
- old.each('SELECT * FROM IpAssignment WHERE ipVersion = 4 OR ipVersion = 6',function(err,row) {
- var network = networks[row.networkId];
- if (network) {
- var member = network._members[row.nodeId];
- if ((member)&&((member.authorized)||(!network['private']))) { // don't mirror assignments to unauthorized members to avoid conflicts
- if (row.ipVersion == 4) {
- member.ipAssignments.push(blobToIPv4(row.ip));
- ++ipAssignmentCount;
- } else if (row.ipVersion == 6) {
- member.ipAssignments.push(blobToIPv6(row.ip));
- ++ipAssignmentCount;
- }
- }
- }
- },nextStep);
-
-},function(nextStep) {
-
- // Old versions only supported Ethertype whitelisting, so that's
- // all we mirror forward. The other fields were always unused.
-
- console.log(' '+ipAssignmentCount+' IP assignments for '+memberCount+' authorized members of '+networkCount+' networks.');
- console.log('Reading allowed Ethernet types (old basic rules)...');
- var etherTypesByNetwork = {};
- old.each('SELECT DISTINCT networkId,ruleNo,etherType FROM Rule WHERE "action" = \'accept\'',function(err,row) {
- if (row.networkId in networks) {
- var et = parseInt(row.etherType)||0;
- var ets = etherTypesByNetwork[row.networkId];
- if (!ets)
- etherTypesByNetwork[row.networkId] = [ et ];
- else ets.push(et);
- }
- },function(err) {
- if (err) return nextStep(err);
- for(var nwid in etherTypesByNetwork) {
- var ets = etherTypesByNetwork[nwid].sort();
- var network = networks[nwid];
- if (network) {
- var rules = [];
- if (ets.indexOf(0) >= 0) {
- // If 0 is in the list, all Ethernet types are allowed so we accept all.
- rules.push({ 'type': 'ACTION_ACCEPT' });
- } else {
- // Otherwise we whitelist.
- for(var i=0;i<ets.length;++i) {
- rules.push({
- 'etherType': ets[i],
- 'not': true,
- 'or': false,
- 'type': 'MATCH_ETHERTYPE'
- });
- }
- rules.push({ 'type': 'ACTION_DROP' });
- rules.push({ 'type': 'ACTION_ACCEPT' });
- }
- network.rules = rules;
- ++ruleCount;
- }
- }
- return nextStep(null);
- });
-
-}],function(err) {
-
- if (err) {
- console.log('FATAL: '+err.toString());
- return process.exit(1);
- }
-
- console.log(' '+ruleCount+' ethernet type whitelists converted to new format rules.');
- old.close();
- console.log('Done reading and converting Sqlite3 database! Writing JSONDB files...');
-
- try {
- fs.mkdirSync(newDbPath,0o700);
- } catch (e) {}
- var nwBase = newDbPath+'/network';
- try {
- fs.mkdirSync(nwBase,0o700);
- } catch (e) {}
- nwBase = nwBase + '/';
- var nwids = Object.keys(networks).sort();
- var fileCount = 0;
- for(var ni=0;ni<nwids.length;++ni) {
- var network = networks[nwids[ni]];
-
- var mids = Object.keys(network._members).sort();
- if (mids.length > 0) {
- try {
- fs.mkdirSync(nwBase+network.id);
- } catch (e) {}
- var mbase = nwBase+network.id+'/member';
- try {
- fs.mkdirSync(mbase,0o700);
- } catch (e) {}
- mbase = mbase + '/';
-
- for(var mi=0;mi<mids.length;++mi) {
- var member = network._members[mids[mi]];
- fs.writeFileSync(mbase+member.id+'.json',JSON.stringify(member,null,1),{ mode: 0o600 });
- ++fileCount;
- //console.log(mbase+member.id+'.json');
- }
- }
-
- delete network._members; // temporary field, not part of actual JSONDB, so don't write
- fs.writeFileSync(nwBase+network.id+'.json',JSON.stringify(network,null,1),{ mode: 0o600 });
- ++fileCount;
- //console.log(nwBase+network.id+'.json');
- }
-
- console.log('');
- console.log('SUCCESS! Wrote '+fileCount+' JSONDB files.');
-
- console.log('');
- console.log('You should still inspect the new DB before going live. Also be sure');
- console.log('to "chown -R" and "chgrp -R" the new DB to the user and group under');
- console.log('which the ZeroTier One instance acting as controller will be running.');
- console.log('The controller must be able to read and write the DB, of course.');
- console.log('');
- console.log('Have fun!');
-
- return process.exit(0);
-});
diff --git a/controller/migrate-sqlite/package.json b/controller/migrate-sqlite/package.json
deleted file mode 100644
index 0dac008f..00000000
--- a/controller/migrate-sqlite/package.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "name": "migrate-sqlite",
- "version": "1.0.0",
- "description": "Migrate old SQLite to new JSON filesystem DB for ZeroTier network controller",
- "main": "migrate.js",
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
- },
- "author": "Adam Ierymenko <adam.ierymenko@zerotier.com>",
- "license": "GPL-3.0",
- "dependencies": {
- "async": "^2.1.4",
- "sqlite3": "^3.1.8"
- }
-}
diff --git a/debian/changelog b/debian/changelog
index 33843150..b13e179a 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+zerotier-one (1.2.6) unstable; urgency=medium
+
+ * See https://github.com/zerotier/ZeroTierOne for release notes.
+
+ -- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 17 Apr 2018 01:00:00 -0700
+
zerotier-one (1.2.4) unstable; urgency=medium
* See https://github.com/zerotier/ZeroTierOne for release notes.
diff --git a/debian/rules b/debian/rules
index 0ef81e05..e6644d8e 100755
--- a/debian/rules
+++ b/debian/rules
@@ -7,7 +7,7 @@ CXXFLAGS=-O3 -fstack-protector-strong
dh $@ --with systemd
override_dh_auto_build:
- make -j 2
+ make -j 4
override_dh_systemd_start:
dh_systemd_start --restart-after-upgrade
diff --git a/ext/bin/tap-windows-ndis6/certutil.exe b/ext/bin/tap-windows-ndis6/certutil.exe
new file mode 100644
index 00000000..b9a0a09c
--- /dev/null
+++ b/ext/bin/tap-windows-ndis6/certutil.exe
Binary files differ
diff --git a/ext/bin/tap-windows-ndis6/x64/ZeroTierOne_NDIS6_x64.msi b/ext/bin/tap-windows-ndis6/x64/ZeroTierOne_NDIS6_x64.msi
index 4cfb7d77..17fe08c2 100644
--- a/ext/bin/tap-windows-ndis6/x64/ZeroTierOne_NDIS6_x64.msi
+++ b/ext/bin/tap-windows-ndis6/x64/ZeroTierOne_NDIS6_x64.msi
Binary files differ
diff --git a/ext/bin/tap-windows-ndis6/x86/ZeroTierOne_NDIS6_x86.msi b/ext/bin/tap-windows-ndis6/x86/ZeroTierOne_NDIS6_x86.msi
index 1b9aec40..415774c9 100644
--- a/ext/bin/tap-windows-ndis6/x86/ZeroTierOne_NDIS6_x86.msi
+++ b/ext/bin/tap-windows-ndis6/x86/ZeroTierOne_NDIS6_x86.msi
Binary files differ
diff --git a/ext/bin/tap-windows-ndis6/zttap300.cer b/ext/bin/tap-windows-ndis6/zttap300.cer
new file mode 100644
index 00000000..ef74e041
--- /dev/null
+++ b/ext/bin/tap-windows-ndis6/zttap300.cer
Binary files differ
diff --git a/ext/ed25519-amd64-asm/batch.c b/ext/ed25519-amd64-asm/batch.c
new file mode 100644
index 00000000..955392ea
--- /dev/null
+++ b/ext/ed25519-amd64-asm/batch.c
@@ -0,0 +1,94 @@
+#include "crypto_sign.h"
+
+#include "crypto_verify_32.h"
+#include "crypto_hash_sha512.h"
+#include "randombytes.h"
+
+#include "ge25519.h"
+#include "hram.h"
+
+#define MAXBATCH 64
+
+int crypto_sign_open_batch(
+ unsigned char* const m[],unsigned long long mlen[],
+ unsigned char* const sm[],const unsigned long long smlen[],
+ unsigned char* const pk[],
+ unsigned long long num
+ )
+{
+ int ret = 0;
+ unsigned long long i, j;
+ shortsc25519 r[MAXBATCH];
+ sc25519 scalars[2*MAXBATCH+1];
+ ge25519 points[2*MAXBATCH+1];
+ unsigned char hram[crypto_hash_sha512_BYTES];
+ unsigned long long batchsize;
+
+ for (i = 0;i < num;++i) mlen[i] = -1;
+
+ while (num >= 3) {
+ batchsize = num;
+ if (batchsize > MAXBATCH) batchsize = MAXBATCH;
+
+ for (i = 0;i < batchsize;++i)
+ if (smlen[i] < 64) goto fallback;
+
+ randombytes((unsigned char*)r,sizeof(shortsc25519) * batchsize);
+
+ /* Computing scalars[0] = ((r1s1 + r2s2 + ...)) */
+ for(i=0;i<batchsize;i++)
+ {
+ sc25519_from32bytes(&scalars[i], sm[i]+32);
+ sc25519_mul_shortsc(&scalars[i], &scalars[i], &r[i]);
+ }
+ for(i=1;i<batchsize;i++)
+ sc25519_add(&scalars[0], &scalars[0], &scalars[i]);
+
+ /* Computing scalars[1] ... scalars[batchsize] as r[i]*H(R[i],A[i],m[i]) */
+ for(i=0;i<batchsize;i++)
+ {
+ get_hram(hram, sm[i], pk[i], m[i], smlen[i]);
+ sc25519_from64bytes(&scalars[i+1],hram);
+ sc25519_mul_shortsc(&scalars[i+1],&scalars[i+1],&r[i]);
+ }
+ /* Setting scalars[batchsize+1] ... scalars[2*batchsize] to r[i] */
+ for(i=0;i<batchsize;i++)
+ sc25519_from_shortsc(&scalars[batchsize+i+1],&r[i]);
+
+ /* Computing points */
+ points[0] = ge25519_base;
+
+ for(i=0;i<batchsize;i++)
+ if (ge25519_unpackneg_vartime(&points[i+1], pk[i])) goto fallback;
+ for(i=0;i<batchsize;i++)
+ if (ge25519_unpackneg_vartime(&points[batchsize+i+1], sm[i])) goto fallback;
+
+ ge25519_multi_scalarmult_vartime(points, points, scalars, 2*batchsize+1);
+
+ if (ge25519_isneutral_vartime(points)) {
+ for(i=0;i<batchsize;i++)
+ {
+ for(j=0;j<smlen[i]-64;j++)
+ m[i][j] = sm[i][j + 64];
+ mlen[i] = smlen[i]-64;
+ }
+ } else {
+ fallback:
+
+ for (i = 0;i < batchsize;++i)
+ ret |= crypto_sign_open(m[i], &mlen[i], sm[i], smlen[i], pk[i]);
+ }
+
+ m += batchsize;
+ mlen += batchsize;
+ sm += batchsize;
+ smlen += batchsize;
+ pk += batchsize;
+ num -= batchsize;
+ }
+
+ for (i = 0;i < num;++i)
+ ret |= crypto_sign_open(m[i], &mlen[i], sm[i], smlen[i], pk[i]);
+
+ return ret;
+}
diff --git a/ext/ed25519-amd64-asm/choose_t.s b/ext/ed25519-amd64-asm/choose_t.s
new file mode 100644
index 00000000..f10d8b8b
--- /dev/null
+++ b/ext/ed25519-amd64-asm/choose_t.s
@@ -0,0 +1,1565 @@
+
+# qhasm: int64 tp
+
+# qhasm: int64 pos
+
+# qhasm: int64 b
+
+# qhasm: int64 basep
+
+# qhasm: input tp
+
+# qhasm: input pos
+
+# qhasm: input b
+
+# qhasm: input basep
+
+# qhasm: int64 mask
+
+# qhasm: int64 u
+
+# qhasm: int64 tysubx0
+
+# qhasm: int64 tysubx1
+
+# qhasm: int64 tysubx2
+
+# qhasm: int64 tysubx3
+
+# qhasm: int64 txaddy0
+
+# qhasm: int64 txaddy1
+
+# qhasm: int64 txaddy2
+
+# qhasm: int64 txaddy3
+
+# qhasm: int64 tt2d0
+
+# qhasm: int64 tt2d1
+
+# qhasm: int64 tt2d2
+
+# qhasm: int64 tt2d3
+
+# qhasm: int64 tt0
+
+# qhasm: int64 tt1
+
+# qhasm: int64 tt2
+
+# qhasm: int64 tt3
+
+# qhasm: int64 subt0
+
+# qhasm: int64 subt1
+
+# qhasm: int64 t
+
+# qhasm: stack64 tp_stack
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_choose_t
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_choose_t
+.globl crypto_sign_ed25519_amd64_64_choose_t
+_crypto_sign_ed25519_amd64_64_choose_t:
+crypto_sign_ed25519_amd64_64_choose_t:
+mov %rsp,%r11
+and $31,%r11
+add $64,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: tp_stack = tp
+# asm 1: movq <tp=int64#1,>tp_stack=stack64#8
+# asm 2: movq <tp=%rdi,>tp_stack=56(%rsp)
+movq %rdi,56(%rsp)
+
+# qhasm: pos *= 768
+# asm 1: imulq $768,<pos=int64#2,>pos=int64#1
+# asm 2: imulq $768,<pos=%rsi,>pos=%rdi
+imulq $768,%rsi,%rdi
+
+# qhasm: mask = b
+# asm 1: mov <b=int64#3,>mask=int64#2
+# asm 2: mov <b=%rdx,>mask=%rsi
+mov %rdx,%rsi
+
+# qhasm: (int64) mask >>= 7
+# asm 1: sar $7,<mask=int64#2
+# asm 2: sar $7,<mask=%rsi
+sar $7,%rsi
+
+# qhasm: u = b
+# asm 1: mov <b=int64#3,>u=int64#5
+# asm 2: mov <b=%rdx,>u=%r8
+mov %rdx,%r8
+
+# qhasm: u += mask
+# asm 1: add <mask=int64#2,<u=int64#5
+# asm 2: add <mask=%rsi,<u=%r8
+add %rsi,%r8
+
+# qhasm: u ^= mask
+# asm 1: xor <mask=int64#2,<u=int64#5
+# asm 2: xor <mask=%rsi,<u=%r8
+xor %rsi,%r8
+
+# qhasm: tysubx0 = 1
+# asm 1: mov $1,>tysubx0=int64#2
+# asm 2: mov $1,>tysubx0=%rsi
+mov $1,%rsi
+
+# qhasm: tysubx1 = 0
+# asm 1: mov $0,>tysubx1=int64#6
+# asm 2: mov $0,>tysubx1=%r9
+mov $0,%r9
+
+# qhasm: tysubx2 = 0
+# asm 1: mov $0,>tysubx2=int64#7
+# asm 2: mov $0,>tysubx2=%rax
+mov $0,%rax
+
+# qhasm: tysubx3 = 0
+# asm 1: mov $0,>tysubx3=int64#8
+# asm 2: mov $0,>tysubx3=%r10
+mov $0,%r10
+
+# qhasm: txaddy0 = 1
+# asm 1: mov $1,>txaddy0=int64#9
+# asm 2: mov $1,>txaddy0=%r11
+mov $1,%r11
+
+# qhasm: txaddy1 = 0
+# asm 1: mov $0,>txaddy1=int64#10
+# asm 2: mov $0,>txaddy1=%r12
+mov $0,%r12
+
+# qhasm: txaddy2 = 0
+# asm 1: mov $0,>txaddy2=int64#11
+# asm 2: mov $0,>txaddy2=%r13
+mov $0,%r13
+
+# qhasm: txaddy3 = 0
+# asm 1: mov $0,>txaddy3=int64#12
+# asm 2: mov $0,>txaddy3=%r14
+mov $0,%r14
+
+# qhasm: =? u - 1
+# asm 1: cmp $1,<u=int64#5
+# asm 2: cmp $1,<u=%r8
+cmp $1,%r8
+
+# qhasm: t = *(uint64 *)(basep + 0 + pos)
+# asm 1: movq 0(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 0(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 0(%rcx,%rdi),%r15
+
+# qhasm: tysubx0 = t if =
+# asm 1: cmove <t=int64#13,<tysubx0=int64#2
+# asm 2: cmove <t=%r15,<tysubx0=%rsi
+cmove %r15,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 8 + pos)
+# asm 1: movq 8(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 8(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 8(%rcx,%rdi),%r15
+
+# qhasm: tysubx1 = t if =
+# asm 1: cmove <t=int64#13,<tysubx1=int64#6
+# asm 2: cmove <t=%r15,<tysubx1=%r9
+cmove %r15,%r9
+
+# qhasm: t = *(uint64 *)(basep + 16 + pos)
+# asm 1: movq 16(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 16(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 16(%rcx,%rdi),%r15
+
+# qhasm: tysubx2 = t if =
+# asm 1: cmove <t=int64#13,<tysubx2=int64#7
+# asm 2: cmove <t=%r15,<tysubx2=%rax
+cmove %r15,%rax
+
+# qhasm: t = *(uint64 *)(basep + 24 + pos)
+# asm 1: movq 24(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 24(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 24(%rcx,%rdi),%r15
+
+# qhasm: tysubx3 = t if =
+# asm 1: cmove <t=int64#13,<tysubx3=int64#8
+# asm 2: cmove <t=%r15,<tysubx3=%r10
+cmove %r15,%r10
+
+# qhasm: t = *(uint64 *)(basep + 32 + pos)
+# asm 1: movq 32(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 32(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 32(%rcx,%rdi),%r15
+
+# qhasm: txaddy0 = t if =
+# asm 1: cmove <t=int64#13,<txaddy0=int64#9
+# asm 2: cmove <t=%r15,<txaddy0=%r11
+cmove %r15,%r11
+
+# qhasm: t = *(uint64 *)(basep + 40 + pos)
+# asm 1: movq 40(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 40(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 40(%rcx,%rdi),%r15
+
+# qhasm: txaddy1 = t if =
+# asm 1: cmove <t=int64#13,<txaddy1=int64#10
+# asm 2: cmove <t=%r15,<txaddy1=%r12
+cmove %r15,%r12
+
+# qhasm: t = *(uint64 *)(basep + 48 + pos)
+# asm 1: movq 48(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 48(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 48(%rcx,%rdi),%r15
+
+# qhasm: txaddy2 = t if =
+# asm 1: cmove <t=int64#13,<txaddy2=int64#11
+# asm 2: cmove <t=%r15,<txaddy2=%r13
+cmove %r15,%r13
+
+# qhasm: t = *(uint64 *)(basep + 56 + pos)
+# asm 1: movq 56(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 56(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 56(%rcx,%rdi),%r15
+
+# qhasm: txaddy3 = t if =
+# asm 1: cmove <t=int64#13,<txaddy3=int64#12
+# asm 2: cmove <t=%r15,<txaddy3=%r14
+cmove %r15,%r14
+
+# qhasm: =? u - 2
+# asm 1: cmp $2,<u=int64#5
+# asm 2: cmp $2,<u=%r8
+cmp $2,%r8
+
+# qhasm: t = *(uint64 *)(basep + 96 + pos)
+# asm 1: movq 96(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 96(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 96(%rcx,%rdi),%r15
+
+# qhasm: tysubx0 = t if =
+# asm 1: cmove <t=int64#13,<tysubx0=int64#2
+# asm 2: cmove <t=%r15,<tysubx0=%rsi
+cmove %r15,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 104 + pos)
+# asm 1: movq 104(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 104(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 104(%rcx,%rdi),%r15
+
+# qhasm: tysubx1 = t if =
+# asm 1: cmove <t=int64#13,<tysubx1=int64#6
+# asm 2: cmove <t=%r15,<tysubx1=%r9
+cmove %r15,%r9
+
+# qhasm: t = *(uint64 *)(basep + 112 + pos)
+# asm 1: movq 112(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 112(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 112(%rcx,%rdi),%r15
+
+# qhasm: tysubx2 = t if =
+# asm 1: cmove <t=int64#13,<tysubx2=int64#7
+# asm 2: cmove <t=%r15,<tysubx2=%rax
+cmove %r15,%rax
+
+# qhasm: t = *(uint64 *)(basep + 120 + pos)
+# asm 1: movq 120(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 120(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 120(%rcx,%rdi),%r15
+
+# qhasm: tysubx3 = t if =
+# asm 1: cmove <t=int64#13,<tysubx3=int64#8
+# asm 2: cmove <t=%r15,<tysubx3=%r10
+cmove %r15,%r10
+
+# qhasm: t = *(uint64 *)(basep + 128 + pos)
+# asm 1: movq 128(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 128(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 128(%rcx,%rdi),%r15
+
+# qhasm: txaddy0 = t if =
+# asm 1: cmove <t=int64#13,<txaddy0=int64#9
+# asm 2: cmove <t=%r15,<txaddy0=%r11
+cmove %r15,%r11
+
+# qhasm: t = *(uint64 *)(basep + 136 + pos)
+# asm 1: movq 136(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 136(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 136(%rcx,%rdi),%r15
+
+# qhasm: txaddy1 = t if =
+# asm 1: cmove <t=int64#13,<txaddy1=int64#10
+# asm 2: cmove <t=%r15,<txaddy1=%r12
+cmove %r15,%r12
+
+# qhasm: t = *(uint64 *)(basep + 144 + pos)
+# asm 1: movq 144(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 144(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 144(%rcx,%rdi),%r15
+
+# qhasm: txaddy2 = t if =
+# asm 1: cmove <t=int64#13,<txaddy2=int64#11
+# asm 2: cmove <t=%r15,<txaddy2=%r13
+cmove %r15,%r13
+
+# qhasm: t = *(uint64 *)(basep + 152 + pos)
+# asm 1: movq 152(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 152(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 152(%rcx,%rdi),%r15
+
+# qhasm: txaddy3 = t if =
+# asm 1: cmove <t=int64#13,<txaddy3=int64#12
+# asm 2: cmove <t=%r15,<txaddy3=%r14
+cmove %r15,%r14
+
+# qhasm: =? u - 3
+# asm 1: cmp $3,<u=int64#5
+# asm 2: cmp $3,<u=%r8
+cmp $3,%r8
+
+# qhasm: t = *(uint64 *)(basep + 192 + pos)
+# asm 1: movq 192(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 192(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 192(%rcx,%rdi),%r15
+
+# qhasm: tysubx0 = t if =
+# asm 1: cmove <t=int64#13,<tysubx0=int64#2
+# asm 2: cmove <t=%r15,<tysubx0=%rsi
+cmove %r15,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 200 + pos)
+# asm 1: movq 200(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 200(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 200(%rcx,%rdi),%r15
+
+# qhasm: tysubx1 = t if =
+# asm 1: cmove <t=int64#13,<tysubx1=int64#6
+# asm 2: cmove <t=%r15,<tysubx1=%r9
+cmove %r15,%r9
+
+# qhasm: t = *(uint64 *)(basep + 208 + pos)
+# asm 1: movq 208(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 208(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 208(%rcx,%rdi),%r15
+
+# qhasm: tysubx2 = t if =
+# asm 1: cmove <t=int64#13,<tysubx2=int64#7
+# asm 2: cmove <t=%r15,<tysubx2=%rax
+cmove %r15,%rax
+
+# qhasm: t = *(uint64 *)(basep + 216 + pos)
+# asm 1: movq 216(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 216(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 216(%rcx,%rdi),%r15
+
+# qhasm: tysubx3 = t if =
+# asm 1: cmove <t=int64#13,<tysubx3=int64#8
+# asm 2: cmove <t=%r15,<tysubx3=%r10
+cmove %r15,%r10
+
+# qhasm: t = *(uint64 *)(basep + 224 + pos)
+# asm 1: movq 224(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 224(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 224(%rcx,%rdi),%r15
+
+# qhasm: txaddy0 = t if =
+# asm 1: cmove <t=int64#13,<txaddy0=int64#9
+# asm 2: cmove <t=%r15,<txaddy0=%r11
+cmove %r15,%r11
+
+# qhasm: t = *(uint64 *)(basep + 232 + pos)
+# asm 1: movq 232(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 232(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 232(%rcx,%rdi),%r15
+
+# qhasm: txaddy1 = t if =
+# asm 1: cmove <t=int64#13,<txaddy1=int64#10
+# asm 2: cmove <t=%r15,<txaddy1=%r12
+cmove %r15,%r12
+
+# qhasm: t = *(uint64 *)(basep + 240 + pos)
+# asm 1: movq 240(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 240(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 240(%rcx,%rdi),%r15
+
+# qhasm: txaddy2 = t if =
+# asm 1: cmove <t=int64#13,<txaddy2=int64#11
+# asm 2: cmove <t=%r15,<txaddy2=%r13
+cmove %r15,%r13
+
+# qhasm: t = *(uint64 *)(basep + 248 + pos)
+# asm 1: movq 248(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 248(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 248(%rcx,%rdi),%r15
+
+# qhasm: txaddy3 = t if =
+# asm 1: cmove <t=int64#13,<txaddy3=int64#12
+# asm 2: cmove <t=%r15,<txaddy3=%r14
+cmove %r15,%r14
+
+# qhasm: =? u - 4
+# asm 1: cmp $4,<u=int64#5
+# asm 2: cmp $4,<u=%r8
+cmp $4,%r8
+
+# qhasm: t = *(uint64 *)(basep + 288 + pos)
+# asm 1: movq 288(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 288(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 288(%rcx,%rdi),%r15
+
+# qhasm: tysubx0 = t if =
+# asm 1: cmove <t=int64#13,<tysubx0=int64#2
+# asm 2: cmove <t=%r15,<tysubx0=%rsi
+cmove %r15,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 296 + pos)
+# asm 1: movq 296(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 296(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 296(%rcx,%rdi),%r15
+
+# qhasm: tysubx1 = t if =
+# asm 1: cmove <t=int64#13,<tysubx1=int64#6
+# asm 2: cmove <t=%r15,<tysubx1=%r9
+cmove %r15,%r9
+
+# qhasm: t = *(uint64 *)(basep + 304 + pos)
+# asm 1: movq 304(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 304(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 304(%rcx,%rdi),%r15
+
+# qhasm: tysubx2 = t if =
+# asm 1: cmove <t=int64#13,<tysubx2=int64#7
+# asm 2: cmove <t=%r15,<tysubx2=%rax
+cmove %r15,%rax
+
+# qhasm: t = *(uint64 *)(basep + 312 + pos)
+# asm 1: movq 312(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 312(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 312(%rcx,%rdi),%r15
+
+# qhasm: tysubx3 = t if =
+# asm 1: cmove <t=int64#13,<tysubx3=int64#8
+# asm 2: cmove <t=%r15,<tysubx3=%r10
+cmove %r15,%r10
+
+# qhasm: t = *(uint64 *)(basep + 320 + pos)
+# asm 1: movq 320(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 320(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 320(%rcx,%rdi),%r15
+
+# qhasm: txaddy0 = t if =
+# asm 1: cmove <t=int64#13,<txaddy0=int64#9
+# asm 2: cmove <t=%r15,<txaddy0=%r11
+cmove %r15,%r11
+
+# qhasm: t = *(uint64 *)(basep + 328 + pos)
+# asm 1: movq 328(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 328(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 328(%rcx,%rdi),%r15
+
+# qhasm: txaddy1 = t if =
+# asm 1: cmove <t=int64#13,<txaddy1=int64#10
+# asm 2: cmove <t=%r15,<txaddy1=%r12
+cmove %r15,%r12
+
+# qhasm: t = *(uint64 *)(basep + 336 + pos)
+# asm 1: movq 336(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 336(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 336(%rcx,%rdi),%r15
+
+# qhasm: txaddy2 = t if =
+# asm 1: cmove <t=int64#13,<txaddy2=int64#11
+# asm 2: cmove <t=%r15,<txaddy2=%r13
+cmove %r15,%r13
+
+# qhasm: t = *(uint64 *)(basep + 344 + pos)
+# asm 1: movq 344(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 344(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 344(%rcx,%rdi),%r15
+
+# qhasm: txaddy3 = t if =
+# asm 1: cmove <t=int64#13,<txaddy3=int64#12
+# asm 2: cmove <t=%r15,<txaddy3=%r14
+cmove %r15,%r14
+
+# qhasm: =? u - 5
+# asm 1: cmp $5,<u=int64#5
+# asm 2: cmp $5,<u=%r8
+cmp $5,%r8
+
+# qhasm: t = *(uint64 *)(basep + 384 + pos)
+# asm 1: movq 384(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 384(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 384(%rcx,%rdi),%r15
+
+# qhasm: tysubx0 = t if =
+# asm 1: cmove <t=int64#13,<tysubx0=int64#2
+# asm 2: cmove <t=%r15,<tysubx0=%rsi
+cmove %r15,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 392 + pos)
+# asm 1: movq 392(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 392(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 392(%rcx,%rdi),%r15
+
+# qhasm: tysubx1 = t if =
+# asm 1: cmove <t=int64#13,<tysubx1=int64#6
+# asm 2: cmove <t=%r15,<tysubx1=%r9
+cmove %r15,%r9
+
+# qhasm: t = *(uint64 *)(basep + 400 + pos)
+# asm 1: movq 400(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 400(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 400(%rcx,%rdi),%r15
+
+# qhasm: tysubx2 = t if =
+# asm 1: cmove <t=int64#13,<tysubx2=int64#7
+# asm 2: cmove <t=%r15,<tysubx2=%rax
+cmove %r15,%rax
+
+# qhasm: t = *(uint64 *)(basep + 408 + pos)
+# asm 1: movq 408(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 408(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 408(%rcx,%rdi),%r15
+
+# qhasm: tysubx3 = t if =
+# asm 1: cmove <t=int64#13,<tysubx3=int64#8
+# asm 2: cmove <t=%r15,<tysubx3=%r10
+cmove %r15,%r10
+
+# qhasm: t = *(uint64 *)(basep + 416 + pos)
+# asm 1: movq 416(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 416(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 416(%rcx,%rdi),%r15
+
+# qhasm: txaddy0 = t if =
+# asm 1: cmove <t=int64#13,<txaddy0=int64#9
+# asm 2: cmove <t=%r15,<txaddy0=%r11
+cmove %r15,%r11
+
+# qhasm: t = *(uint64 *)(basep + 424 + pos)
+# asm 1: movq 424(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 424(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 424(%rcx,%rdi),%r15
+
+# qhasm: txaddy1 = t if =
+# asm 1: cmove <t=int64#13,<txaddy1=int64#10
+# asm 2: cmove <t=%r15,<txaddy1=%r12
+cmove %r15,%r12
+
+# qhasm: t = *(uint64 *)(basep + 432 + pos)
+# asm 1: movq 432(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 432(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 432(%rcx,%rdi),%r15
+
+# qhasm: txaddy2 = t if =
+# asm 1: cmove <t=int64#13,<txaddy2=int64#11
+# asm 2: cmove <t=%r15,<txaddy2=%r13
+cmove %r15,%r13
+
+# qhasm: t = *(uint64 *)(basep + 440 + pos)
+# asm 1: movq 440(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 440(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 440(%rcx,%rdi),%r15
+
+# qhasm: txaddy3 = t if =
+# asm 1: cmove <t=int64#13,<txaddy3=int64#12
+# asm 2: cmove <t=%r15,<txaddy3=%r14
+cmove %r15,%r14
+
+# qhasm: =? u - 6
+# asm 1: cmp $6,<u=int64#5
+# asm 2: cmp $6,<u=%r8
+cmp $6,%r8
+
+# qhasm: t = *(uint64 *)(basep + 480 + pos)
+# asm 1: movq 480(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 480(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 480(%rcx,%rdi),%r15
+
+# qhasm: tysubx0 = t if =
+# asm 1: cmove <t=int64#13,<tysubx0=int64#2
+# asm 2: cmove <t=%r15,<tysubx0=%rsi
+cmove %r15,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 488 + pos)
+# asm 1: movq 488(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 488(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 488(%rcx,%rdi),%r15
+
+# qhasm: tysubx1 = t if =
+# asm 1: cmove <t=int64#13,<tysubx1=int64#6
+# asm 2: cmove <t=%r15,<tysubx1=%r9
+cmove %r15,%r9
+
+# qhasm: t = *(uint64 *)(basep + 496 + pos)
+# asm 1: movq 496(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 496(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 496(%rcx,%rdi),%r15
+
+# qhasm: tysubx2 = t if =
+# asm 1: cmove <t=int64#13,<tysubx2=int64#7
+# asm 2: cmove <t=%r15,<tysubx2=%rax
+cmove %r15,%rax
+
+# qhasm: t = *(uint64 *)(basep + 504 + pos)
+# asm 1: movq 504(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 504(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 504(%rcx,%rdi),%r15
+
+# qhasm: tysubx3 = t if =
+# asm 1: cmove <t=int64#13,<tysubx3=int64#8
+# asm 2: cmove <t=%r15,<tysubx3=%r10
+cmove %r15,%r10
+
+# qhasm: t = *(uint64 *)(basep + 512 + pos)
+# asm 1: movq 512(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 512(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 512(%rcx,%rdi),%r15
+
+# qhasm: txaddy0 = t if =
+# asm 1: cmove <t=int64#13,<txaddy0=int64#9
+# asm 2: cmove <t=%r15,<txaddy0=%r11
+cmove %r15,%r11
+
+# qhasm: t = *(uint64 *)(basep + 520 + pos)
+# asm 1: movq 520(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 520(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 520(%rcx,%rdi),%r15
+
+# qhasm: txaddy1 = t if =
+# asm 1: cmove <t=int64#13,<txaddy1=int64#10
+# asm 2: cmove <t=%r15,<txaddy1=%r12
+cmove %r15,%r12
+
+# qhasm: t = *(uint64 *)(basep + 528 + pos)
+# asm 1: movq 528(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 528(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 528(%rcx,%rdi),%r15
+
+# qhasm: txaddy2 = t if =
+# asm 1: cmove <t=int64#13,<txaddy2=int64#11
+# asm 2: cmove <t=%r15,<txaddy2=%r13
+cmove %r15,%r13
+
+# qhasm: t = *(uint64 *)(basep + 536 + pos)
+# asm 1: movq 536(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 536(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 536(%rcx,%rdi),%r15
+
+# qhasm: txaddy3 = t if =
+# asm 1: cmove <t=int64#13,<txaddy3=int64#12
+# asm 2: cmove <t=%r15,<txaddy3=%r14
+cmove %r15,%r14
+
+# qhasm: =? u - 7
+# asm 1: cmp $7,<u=int64#5
+# asm 2: cmp $7,<u=%r8
+cmp $7,%r8
+
+# qhasm: t = *(uint64 *)(basep + 576 + pos)
+# asm 1: movq 576(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 576(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 576(%rcx,%rdi),%r15
+
+# qhasm: tysubx0 = t if =
+# asm 1: cmove <t=int64#13,<tysubx0=int64#2
+# asm 2: cmove <t=%r15,<tysubx0=%rsi
+cmove %r15,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 584 + pos)
+# asm 1: movq 584(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 584(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 584(%rcx,%rdi),%r15
+
+# qhasm: tysubx1 = t if =
+# asm 1: cmove <t=int64#13,<tysubx1=int64#6
+# asm 2: cmove <t=%r15,<tysubx1=%r9
+cmove %r15,%r9
+
+# qhasm: t = *(uint64 *)(basep + 592 + pos)
+# asm 1: movq 592(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 592(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 592(%rcx,%rdi),%r15
+
+# qhasm: tysubx2 = t if =
+# asm 1: cmove <t=int64#13,<tysubx2=int64#7
+# asm 2: cmove <t=%r15,<tysubx2=%rax
+cmove %r15,%rax
+
+# qhasm: t = *(uint64 *)(basep + 600 + pos)
+# asm 1: movq 600(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 600(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 600(%rcx,%rdi),%r15
+
+# qhasm: tysubx3 = t if =
+# asm 1: cmove <t=int64#13,<tysubx3=int64#8
+# asm 2: cmove <t=%r15,<tysubx3=%r10
+cmove %r15,%r10
+
+# qhasm: t = *(uint64 *)(basep + 608 + pos)
+# asm 1: movq 608(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 608(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 608(%rcx,%rdi),%r15
+
+# qhasm: txaddy0 = t if =
+# asm 1: cmove <t=int64#13,<txaddy0=int64#9
+# asm 2: cmove <t=%r15,<txaddy0=%r11
+cmove %r15,%r11
+
+# qhasm: t = *(uint64 *)(basep + 616 + pos)
+# asm 1: movq 616(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 616(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 616(%rcx,%rdi),%r15
+
+# qhasm: txaddy1 = t if =
+# asm 1: cmove <t=int64#13,<txaddy1=int64#10
+# asm 2: cmove <t=%r15,<txaddy1=%r12
+cmove %r15,%r12
+
+# qhasm: t = *(uint64 *)(basep + 624 + pos)
+# asm 1: movq 624(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 624(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 624(%rcx,%rdi),%r15
+
+# qhasm: txaddy2 = t if =
+# asm 1: cmove <t=int64#13,<txaddy2=int64#11
+# asm 2: cmove <t=%r15,<txaddy2=%r13
+cmove %r15,%r13
+
+# qhasm: t = *(uint64 *)(basep + 632 + pos)
+# asm 1: movq 632(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 632(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 632(%rcx,%rdi),%r15
+
+# qhasm: txaddy3 = t if =
+# asm 1: cmove <t=int64#13,<txaddy3=int64#12
+# asm 2: cmove <t=%r15,<txaddy3=%r14
+cmove %r15,%r14
+
+# qhasm: =? u - 8
+# asm 1: cmp $8,<u=int64#5
+# asm 2: cmp $8,<u=%r8
+cmp $8,%r8
+
+# qhasm: t = *(uint64 *)(basep + 672 + pos)
+# asm 1: movq 672(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 672(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 672(%rcx,%rdi),%r15
+
+# qhasm: tysubx0 = t if =
+# asm 1: cmove <t=int64#13,<tysubx0=int64#2
+# asm 2: cmove <t=%r15,<tysubx0=%rsi
+cmove %r15,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 680 + pos)
+# asm 1: movq 680(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 680(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 680(%rcx,%rdi),%r15
+
+# qhasm: tysubx1 = t if =
+# asm 1: cmove <t=int64#13,<tysubx1=int64#6
+# asm 2: cmove <t=%r15,<tysubx1=%r9
+cmove %r15,%r9
+
+# qhasm: t = *(uint64 *)(basep + 688 + pos)
+# asm 1: movq 688(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 688(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 688(%rcx,%rdi),%r15
+
+# qhasm: tysubx2 = t if =
+# asm 1: cmove <t=int64#13,<tysubx2=int64#7
+# asm 2: cmove <t=%r15,<tysubx2=%rax
+cmove %r15,%rax
+
+# qhasm: t = *(uint64 *)(basep + 696 + pos)
+# asm 1: movq 696(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 696(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 696(%rcx,%rdi),%r15
+
+# qhasm: tysubx3 = t if =
+# asm 1: cmove <t=int64#13,<tysubx3=int64#8
+# asm 2: cmove <t=%r15,<tysubx3=%r10
+cmove %r15,%r10
+
+# qhasm: t = *(uint64 *)(basep + 704 + pos)
+# asm 1: movq 704(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 704(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 704(%rcx,%rdi),%r15
+
+# qhasm: txaddy0 = t if =
+# asm 1: cmove <t=int64#13,<txaddy0=int64#9
+# asm 2: cmove <t=%r15,<txaddy0=%r11
+cmove %r15,%r11
+
+# qhasm: t = *(uint64 *)(basep + 712 + pos)
+# asm 1: movq 712(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 712(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 712(%rcx,%rdi),%r15
+
+# qhasm: txaddy1 = t if =
+# asm 1: cmove <t=int64#13,<txaddy1=int64#10
+# asm 2: cmove <t=%r15,<txaddy1=%r12
+cmove %r15,%r12
+
+# qhasm: t = *(uint64 *)(basep + 720 + pos)
+# asm 1: movq 720(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 720(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 720(%rcx,%rdi),%r15
+
+# qhasm: txaddy2 = t if =
+# asm 1: cmove <t=int64#13,<txaddy2=int64#11
+# asm 2: cmove <t=%r15,<txaddy2=%r13
+cmove %r15,%r13
+
+# qhasm: t = *(uint64 *)(basep + 728 + pos)
+# asm 1: movq 728(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 728(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 728(%rcx,%rdi),%r15
+
+# qhasm: txaddy3 = t if =
+# asm 1: cmove <t=int64#13,<txaddy3=int64#12
+# asm 2: cmove <t=%r15,<txaddy3=%r14
+cmove %r15,%r14
+
+# qhasm: signed<? b - 0
+# asm 1: cmp $0,<b=int64#3
+# asm 2: cmp $0,<b=%rdx
+cmp $0,%rdx
+
+# qhasm: t = tysubx0
+# asm 1: mov <tysubx0=int64#2,>t=int64#13
+# asm 2: mov <tysubx0=%rsi,>t=%r15
+mov %rsi,%r15
+
+# qhasm: tysubx0 = txaddy0 if signed<
+# asm 1: cmovl <txaddy0=int64#9,<tysubx0=int64#2
+# asm 2: cmovl <txaddy0=%r11,<tysubx0=%rsi
+cmovl %r11,%rsi
+
+# qhasm: txaddy0 = t if signed<
+# asm 1: cmovl <t=int64#13,<txaddy0=int64#9
+# asm 2: cmovl <t=%r15,<txaddy0=%r11
+cmovl %r15,%r11
+
+# qhasm: t = tysubx1
+# asm 1: mov <tysubx1=int64#6,>t=int64#13
+# asm 2: mov <tysubx1=%r9,>t=%r15
+mov %r9,%r15
+
+# qhasm: tysubx1 = txaddy1 if signed<
+# asm 1: cmovl <txaddy1=int64#10,<tysubx1=int64#6
+# asm 2: cmovl <txaddy1=%r12,<tysubx1=%r9
+cmovl %r12,%r9
+
+# qhasm: txaddy1 = t if signed<
+# asm 1: cmovl <t=int64#13,<txaddy1=int64#10
+# asm 2: cmovl <t=%r15,<txaddy1=%r12
+cmovl %r15,%r12
+
+# qhasm: t = tysubx2
+# asm 1: mov <tysubx2=int64#7,>t=int64#13
+# asm 2: mov <tysubx2=%rax,>t=%r15
+mov %rax,%r15
+
+# qhasm: tysubx2 = txaddy2 if signed<
+# asm 1: cmovl <txaddy2=int64#11,<tysubx2=int64#7
+# asm 2: cmovl <txaddy2=%r13,<tysubx2=%rax
+cmovl %r13,%rax
+
+# qhasm: txaddy2 = t if signed<
+# asm 1: cmovl <t=int64#13,<txaddy2=int64#11
+# asm 2: cmovl <t=%r15,<txaddy2=%r13
+cmovl %r15,%r13
+
+# qhasm: t = tysubx3
+# asm 1: mov <tysubx3=int64#8,>t=int64#13
+# asm 2: mov <tysubx3=%r10,>t=%r15
+mov %r10,%r15
+
+# qhasm: tysubx3 = txaddy3 if signed<
+# asm 1: cmovl <txaddy3=int64#12,<tysubx3=int64#8
+# asm 2: cmovl <txaddy3=%r14,<tysubx3=%r10
+cmovl %r14,%r10
+
+# qhasm: txaddy3 = t if signed<
+# asm 1: cmovl <t=int64#13,<txaddy3=int64#12
+# asm 2: cmovl <t=%r15,<txaddy3=%r14
+cmovl %r15,%r14
+
+# qhasm: tp = tp_stack
+# asm 1: movq <tp_stack=stack64#8,>tp=int64#13
+# asm 2: movq <tp_stack=56(%rsp),>tp=%r15
+movq 56(%rsp),%r15
+
+# qhasm: *(uint64 *)(tp + 0) = tysubx0
+# asm 1: movq <tysubx0=int64#2,0(<tp=int64#13)
+# asm 2: movq <tysubx0=%rsi,0(<tp=%r15)
+movq %rsi,0(%r15)
+
+# qhasm: *(uint64 *)(tp + 8) = tysubx1
+# asm 1: movq <tysubx1=int64#6,8(<tp=int64#13)
+# asm 2: movq <tysubx1=%r9,8(<tp=%r15)
+movq %r9,8(%r15)
+
+# qhasm: *(uint64 *)(tp + 16) = tysubx2
+# asm 1: movq <tysubx2=int64#7,16(<tp=int64#13)
+# asm 2: movq <tysubx2=%rax,16(<tp=%r15)
+movq %rax,16(%r15)
+
+# qhasm: *(uint64 *)(tp + 24) = tysubx3
+# asm 1: movq <tysubx3=int64#8,24(<tp=int64#13)
+# asm 2: movq <tysubx3=%r10,24(<tp=%r15)
+movq %r10,24(%r15)
+
+# qhasm: *(uint64 *)(tp + 32) = txaddy0
+# asm 1: movq <txaddy0=int64#9,32(<tp=int64#13)
+# asm 2: movq <txaddy0=%r11,32(<tp=%r15)
+movq %r11,32(%r15)
+
+# qhasm: *(uint64 *)(tp + 40) = txaddy1
+# asm 1: movq <txaddy1=int64#10,40(<tp=int64#13)
+# asm 2: movq <txaddy1=%r12,40(<tp=%r15)
+movq %r12,40(%r15)
+
+# qhasm: *(uint64 *)(tp + 48) = txaddy2
+# asm 1: movq <txaddy2=int64#11,48(<tp=int64#13)
+# asm 2: movq <txaddy2=%r13,48(<tp=%r15)
+movq %r13,48(%r15)
+
+# qhasm: *(uint64 *)(tp + 56) = txaddy3
+# asm 1: movq <txaddy3=int64#12,56(<tp=int64#13)
+# asm 2: movq <txaddy3=%r14,56(<tp=%r15)
+movq %r14,56(%r15)
+
+# qhasm: tt2d0 = 0
+# asm 1: mov $0,>tt2d0=int64#2
+# asm 2: mov $0,>tt2d0=%rsi
+mov $0,%rsi
+
+# qhasm: tt2d1 = 0
+# asm 1: mov $0,>tt2d1=int64#6
+# asm 2: mov $0,>tt2d1=%r9
+mov $0,%r9
+
+# qhasm: tt2d2 = 0
+# asm 1: mov $0,>tt2d2=int64#7
+# asm 2: mov $0,>tt2d2=%rax
+mov $0,%rax
+
+# qhasm: tt2d3 = 0
+# asm 1: mov $0,>tt2d3=int64#8
+# asm 2: mov $0,>tt2d3=%r10
+mov $0,%r10
+
+# qhasm: =? u - 1
+# asm 1: cmp $1,<u=int64#5
+# asm 2: cmp $1,<u=%r8
+cmp $1,%r8
+
+# qhasm: t = *(uint64 *)(basep + 64 + pos)
+# asm 1: movq 64(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 64(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 64(%rcx,%rdi),%r11
+
+# qhasm: tt2d0 = t if =
+# asm 1: cmove <t=int64#9,<tt2d0=int64#2
+# asm 2: cmove <t=%r11,<tt2d0=%rsi
+cmove %r11,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 72 + pos)
+# asm 1: movq 72(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 72(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 72(%rcx,%rdi),%r11
+
+# qhasm: tt2d1 = t if =
+# asm 1: cmove <t=int64#9,<tt2d1=int64#6
+# asm 2: cmove <t=%r11,<tt2d1=%r9
+cmove %r11,%r9
+
+# qhasm: t = *(uint64 *)(basep + 80 + pos)
+# asm 1: movq 80(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 80(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 80(%rcx,%rdi),%r11
+
+# qhasm: tt2d2 = t if =
+# asm 1: cmove <t=int64#9,<tt2d2=int64#7
+# asm 2: cmove <t=%r11,<tt2d2=%rax
+cmove %r11,%rax
+
+# qhasm: t = *(uint64 *)(basep + 88 + pos)
+# asm 1: movq 88(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 88(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 88(%rcx,%rdi),%r11
+
+# qhasm: tt2d3 = t if =
+# asm 1: cmove <t=int64#9,<tt2d3=int64#8
+# asm 2: cmove <t=%r11,<tt2d3=%r10
+cmove %r11,%r10
+
+# qhasm: =? u - 2
+# asm 1: cmp $2,<u=int64#5
+# asm 2: cmp $2,<u=%r8
+cmp $2,%r8
+
+# qhasm: t = *(uint64 *)(basep + 160 + pos)
+# asm 1: movq 160(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 160(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 160(%rcx,%rdi),%r11
+
+# qhasm: tt2d0 = t if =
+# asm 1: cmove <t=int64#9,<tt2d0=int64#2
+# asm 2: cmove <t=%r11,<tt2d0=%rsi
+cmove %r11,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 168 + pos)
+# asm 1: movq 168(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 168(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 168(%rcx,%rdi),%r11
+
+# qhasm: tt2d1 = t if =
+# asm 1: cmove <t=int64#9,<tt2d1=int64#6
+# asm 2: cmove <t=%r11,<tt2d1=%r9
+cmove %r11,%r9
+
+# qhasm: t = *(uint64 *)(basep + 176 + pos)
+# asm 1: movq 176(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 176(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 176(%rcx,%rdi),%r11
+
+# qhasm: tt2d2 = t if =
+# asm 1: cmove <t=int64#9,<tt2d2=int64#7
+# asm 2: cmove <t=%r11,<tt2d2=%rax
+cmove %r11,%rax
+
+# qhasm: t = *(uint64 *)(basep + 184 + pos)
+# asm 1: movq 184(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 184(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 184(%rcx,%rdi),%r11
+
+# qhasm: tt2d3 = t if =
+# asm 1: cmove <t=int64#9,<tt2d3=int64#8
+# asm 2: cmove <t=%r11,<tt2d3=%r10
+cmove %r11,%r10
+
+# qhasm: =? u - 3
+# asm 1: cmp $3,<u=int64#5
+# asm 2: cmp $3,<u=%r8
+cmp $3,%r8
+
+# qhasm: t = *(uint64 *)(basep + 256 + pos)
+# asm 1: movq 256(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 256(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 256(%rcx,%rdi),%r11
+
+# qhasm: tt2d0 = t if =
+# asm 1: cmove <t=int64#9,<tt2d0=int64#2
+# asm 2: cmove <t=%r11,<tt2d0=%rsi
+cmove %r11,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 264 + pos)
+# asm 1: movq 264(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 264(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 264(%rcx,%rdi),%r11
+
+# qhasm: tt2d1 = t if =
+# asm 1: cmove <t=int64#9,<tt2d1=int64#6
+# asm 2: cmove <t=%r11,<tt2d1=%r9
+cmove %r11,%r9
+
+# qhasm: t = *(uint64 *)(basep + 272 + pos)
+# asm 1: movq 272(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 272(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 272(%rcx,%rdi),%r11
+
+# qhasm: tt2d2 = t if =
+# asm 1: cmove <t=int64#9,<tt2d2=int64#7
+# asm 2: cmove <t=%r11,<tt2d2=%rax
+cmove %r11,%rax
+
+# qhasm: t = *(uint64 *)(basep + 280 + pos)
+# asm 1: movq 280(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 280(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 280(%rcx,%rdi),%r11
+
+# qhasm: tt2d3 = t if =
+# asm 1: cmove <t=int64#9,<tt2d3=int64#8
+# asm 2: cmove <t=%r11,<tt2d3=%r10
+cmove %r11,%r10
+
+# qhasm: =? u - 4
+# asm 1: cmp $4,<u=int64#5
+# asm 2: cmp $4,<u=%r8
+cmp $4,%r8
+
+# qhasm: t = *(uint64 *)(basep + 352 + pos)
+# asm 1: movq 352(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 352(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 352(%rcx,%rdi),%r11
+
+# qhasm: tt2d0 = t if =
+# asm 1: cmove <t=int64#9,<tt2d0=int64#2
+# asm 2: cmove <t=%r11,<tt2d0=%rsi
+cmove %r11,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 360 + pos)
+# asm 1: movq 360(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 360(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 360(%rcx,%rdi),%r11
+
+# qhasm: tt2d1 = t if =
+# asm 1: cmove <t=int64#9,<tt2d1=int64#6
+# asm 2: cmove <t=%r11,<tt2d1=%r9
+cmove %r11,%r9
+
+# qhasm: t = *(uint64 *)(basep + 368 + pos)
+# asm 1: movq 368(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 368(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 368(%rcx,%rdi),%r11
+
+# qhasm: tt2d2 = t if =
+# asm 1: cmove <t=int64#9,<tt2d2=int64#7
+# asm 2: cmove <t=%r11,<tt2d2=%rax
+cmove %r11,%rax
+
+# qhasm: t = *(uint64 *)(basep + 376 + pos)
+# asm 1: movq 376(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 376(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 376(%rcx,%rdi),%r11
+
+# qhasm: tt2d3 = t if =
+# asm 1: cmove <t=int64#9,<tt2d3=int64#8
+# asm 2: cmove <t=%r11,<tt2d3=%r10
+cmove %r11,%r10
+
+# qhasm: =? u - 5
+# asm 1: cmp $5,<u=int64#5
+# asm 2: cmp $5,<u=%r8
+cmp $5,%r8
+
+# qhasm: t = *(uint64 *)(basep + 448 + pos)
+# asm 1: movq 448(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 448(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 448(%rcx,%rdi),%r11
+
+# qhasm: tt2d0 = t if =
+# asm 1: cmove <t=int64#9,<tt2d0=int64#2
+# asm 2: cmove <t=%r11,<tt2d0=%rsi
+cmove %r11,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 456 + pos)
+# asm 1: movq 456(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 456(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 456(%rcx,%rdi),%r11
+
+# qhasm: tt2d1 = t if =
+# asm 1: cmove <t=int64#9,<tt2d1=int64#6
+# asm 2: cmove <t=%r11,<tt2d1=%r9
+cmove %r11,%r9
+
+# qhasm: t = *(uint64 *)(basep + 464 + pos)
+# asm 1: movq 464(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 464(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 464(%rcx,%rdi),%r11
+
+# qhasm: tt2d2 = t if =
+# asm 1: cmove <t=int64#9,<tt2d2=int64#7
+# asm 2: cmove <t=%r11,<tt2d2=%rax
+cmove %r11,%rax
+
+# qhasm: t = *(uint64 *)(basep + 472 + pos)
+# asm 1: movq 472(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 472(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 472(%rcx,%rdi),%r11
+
+# qhasm: tt2d3 = t if =
+# asm 1: cmove <t=int64#9,<tt2d3=int64#8
+# asm 2: cmove <t=%r11,<tt2d3=%r10
+cmove %r11,%r10
+
+# qhasm: =? u - 6
+# asm 1: cmp $6,<u=int64#5
+# asm 2: cmp $6,<u=%r8
+cmp $6,%r8
+
+# qhasm: t = *(uint64 *)(basep + 544 + pos)
+# asm 1: movq 544(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 544(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 544(%rcx,%rdi),%r11
+
+# qhasm: tt2d0 = t if =
+# asm 1: cmove <t=int64#9,<tt2d0=int64#2
+# asm 2: cmove <t=%r11,<tt2d0=%rsi
+cmove %r11,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 552 + pos)
+# asm 1: movq 552(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 552(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 552(%rcx,%rdi),%r11
+
+# qhasm: tt2d1 = t if =
+# asm 1: cmove <t=int64#9,<tt2d1=int64#6
+# asm 2: cmove <t=%r11,<tt2d1=%r9
+cmove %r11,%r9
+
+# qhasm: t = *(uint64 *)(basep + 560 + pos)
+# asm 1: movq 560(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 560(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 560(%rcx,%rdi),%r11
+
+# qhasm: tt2d2 = t if =
+# asm 1: cmove <t=int64#9,<tt2d2=int64#7
+# asm 2: cmove <t=%r11,<tt2d2=%rax
+cmove %r11,%rax
+
+# qhasm: t = *(uint64 *)(basep + 568 + pos)
+# asm 1: movq 568(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 568(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 568(%rcx,%rdi),%r11
+
+# qhasm: tt2d3 = t if =
+# asm 1: cmove <t=int64#9,<tt2d3=int64#8
+# asm 2: cmove <t=%r11,<tt2d3=%r10
+cmove %r11,%r10
+
+# qhasm: =? u - 7
+# asm 1: cmp $7,<u=int64#5
+# asm 2: cmp $7,<u=%r8
+cmp $7,%r8
+
+# qhasm: t = *(uint64 *)(basep + 640 + pos)
+# asm 1: movq 640(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 640(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 640(%rcx,%rdi),%r11
+
+# qhasm: tt2d0 = t if =
+# asm 1: cmove <t=int64#9,<tt2d0=int64#2
+# asm 2: cmove <t=%r11,<tt2d0=%rsi
+cmove %r11,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 648 + pos)
+# asm 1: movq 648(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 648(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 648(%rcx,%rdi),%r11
+
+# qhasm: tt2d1 = t if =
+# asm 1: cmove <t=int64#9,<tt2d1=int64#6
+# asm 2: cmove <t=%r11,<tt2d1=%r9
+cmove %r11,%r9
+
+# qhasm: t = *(uint64 *)(basep + 656 + pos)
+# asm 1: movq 656(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 656(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 656(%rcx,%rdi),%r11
+
+# qhasm: tt2d2 = t if =
+# asm 1: cmove <t=int64#9,<tt2d2=int64#7
+# asm 2: cmove <t=%r11,<tt2d2=%rax
+cmove %r11,%rax
+
+# qhasm: t = *(uint64 *)(basep + 664 + pos)
+# asm 1: movq 664(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 664(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 664(%rcx,%rdi),%r11
+
+# qhasm: tt2d3 = t if =
+# asm 1: cmove <t=int64#9,<tt2d3=int64#8
+# asm 2: cmove <t=%r11,<tt2d3=%r10
+cmove %r11,%r10
+
+# qhasm: =? u - 8
+# asm 1: cmp $8,<u=int64#5
+# asm 2: cmp $8,<u=%r8
+cmp $8,%r8
+
+# qhasm: t = *(uint64 *)(basep + 736 + pos)
+# asm 1: movq 736(<basep=int64#4,<pos=int64#1),>t=int64#5
+# asm 2: movq 736(<basep=%rcx,<pos=%rdi),>t=%r8
+movq 736(%rcx,%rdi),%r8
+
+# qhasm: tt2d0 = t if =
+# asm 1: cmove <t=int64#5,<tt2d0=int64#2
+# asm 2: cmove <t=%r8,<tt2d0=%rsi
+cmove %r8,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 744 + pos)
+# asm 1: movq 744(<basep=int64#4,<pos=int64#1),>t=int64#5
+# asm 2: movq 744(<basep=%rcx,<pos=%rdi),>t=%r8
+movq 744(%rcx,%rdi),%r8
+
+# qhasm: tt2d1 = t if =
+# asm 1: cmove <t=int64#5,<tt2d1=int64#6
+# asm 2: cmove <t=%r8,<tt2d1=%r9
+cmove %r8,%r9
+
+# qhasm: t = *(uint64 *)(basep + 752 + pos)
+# asm 1: movq 752(<basep=int64#4,<pos=int64#1),>t=int64#5
+# asm 2: movq 752(<basep=%rcx,<pos=%rdi),>t=%r8
+movq 752(%rcx,%rdi),%r8
+
+# qhasm: tt2d2 = t if =
+# asm 1: cmove <t=int64#5,<tt2d2=int64#7
+# asm 2: cmove <t=%r8,<tt2d2=%rax
+cmove %r8,%rax
+
+# qhasm: t = *(uint64 *)(basep + 760 + pos)
+# asm 1: movq 760(<basep=int64#4,<pos=int64#1),>t=int64#1
+# asm 2: movq 760(<basep=%rcx,<pos=%rdi),>t=%rdi
+movq 760(%rcx,%rdi),%rdi
+
+# qhasm: tt2d3 = t if =
+# asm 1: cmove <t=int64#1,<tt2d3=int64#8
+# asm 2: cmove <t=%rdi,<tt2d3=%r10
+cmove %rdi,%r10
+
+# qhasm: tt0 = 0
+# asm 1: mov $0,>tt0=int64#1
+# asm 2: mov $0,>tt0=%rdi
+mov $0,%rdi
+
+# qhasm: tt1 = 0
+# asm 1: mov $0,>tt1=int64#4
+# asm 2: mov $0,>tt1=%rcx
+mov $0,%rcx
+
+# qhasm: tt2 = 0
+# asm 1: mov $0,>tt2=int64#5
+# asm 2: mov $0,>tt2=%r8
+mov $0,%r8
+
+# qhasm: tt3 = 0
+# asm 1: mov $0,>tt3=int64#9
+# asm 2: mov $0,>tt3=%r11
+mov $0,%r11
+
+# qhasm: carry? tt0 -= tt2d0
+# asm 1: sub <tt2d0=int64#2,<tt0=int64#1
+# asm 2: sub <tt2d0=%rsi,<tt0=%rdi
+sub %rsi,%rdi
+
+# qhasm: carry? tt1 -= tt2d1 - carry
+# asm 1: sbb <tt2d1=int64#6,<tt1=int64#4
+# asm 2: sbb <tt2d1=%r9,<tt1=%rcx
+sbb %r9,%rcx
+
+# qhasm: carry? tt2 -= tt2d2 - carry
+# asm 1: sbb <tt2d2=int64#7,<tt2=int64#5
+# asm 2: sbb <tt2d2=%rax,<tt2=%r8
+sbb %rax,%r8
+
+# qhasm: carry? tt3 -= tt2d3 - carry
+# asm 1: sbb <tt2d3=int64#8,<tt3=int64#9
+# asm 2: sbb <tt2d3=%r10,<tt3=%r11
+sbb %r10,%r11
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#10
+# asm 2: mov $0,>subt0=%r12
+mov $0,%r12
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#11
+# asm 2: mov $38,>subt1=%r13
+mov $38,%r13
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#10,<subt1=int64#11
+# asm 2: cmovae <subt0=%r12,<subt1=%r13
+cmovae %r12,%r13
+
+# qhasm: carry? tt0 -= subt1
+# asm 1: sub <subt1=int64#11,<tt0=int64#1
+# asm 2: sub <subt1=%r13,<tt0=%rdi
+sub %r13,%rdi
+
+# qhasm: carry? tt1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#10,<tt1=int64#4
+# asm 2: sbb <subt0=%r12,<tt1=%rcx
+sbb %r12,%rcx
+
+# qhasm: carry? tt2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#10,<tt2=int64#5
+# asm 2: sbb <subt0=%r12,<tt2=%r8
+sbb %r12,%r8
+
+# qhasm: carry? tt3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#10,<tt3=int64#9
+# asm 2: sbb <subt0=%r12,<tt3=%r11
+sbb %r12,%r11
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#11,<subt0=int64#10
+# asm 2: cmovc <subt1=%r13,<subt0=%r12
+cmovc %r13,%r12
+
+# qhasm: tt0 -= subt0
+# asm 1: sub <subt0=int64#10,<tt0=int64#1
+# asm 2: sub <subt0=%r12,<tt0=%rdi
+sub %r12,%rdi
+
+# qhasm: signed<? b - 0
+# asm 1: cmp $0,<b=int64#3
+# asm 2: cmp $0,<b=%rdx
+cmp $0,%rdx
+
+# qhasm: tt2d0 = tt0 if signed<
+# asm 1: cmovl <tt0=int64#1,<tt2d0=int64#2
+# asm 2: cmovl <tt0=%rdi,<tt2d0=%rsi
+cmovl %rdi,%rsi
+
+# qhasm: tt2d1 = tt1 if signed<
+# asm 1: cmovl <tt1=int64#4,<tt2d1=int64#6
+# asm 2: cmovl <tt1=%rcx,<tt2d1=%r9
+cmovl %rcx,%r9
+
+# qhasm: tt2d2 = tt2 if signed<
+# asm 1: cmovl <tt2=int64#5,<tt2d2=int64#7
+# asm 2: cmovl <tt2=%r8,<tt2d2=%rax
+cmovl %r8,%rax
+
+# qhasm: tt2d3 = tt3 if signed<
+# asm 1: cmovl <tt3=int64#9,<tt2d3=int64#8
+# asm 2: cmovl <tt3=%r11,<tt2d3=%r10
+cmovl %r11,%r10
+
+# qhasm: *(uint64 *)(tp + 64) = tt2d0
+# asm 1: movq <tt2d0=int64#2,64(<tp=int64#13)
+# asm 2: movq <tt2d0=%rsi,64(<tp=%r15)
+movq %rsi,64(%r15)
+
+# qhasm: *(uint64 *)(tp + 72) = tt2d1
+# asm 1: movq <tt2d1=int64#6,72(<tp=int64#13)
+# asm 2: movq <tt2d1=%r9,72(<tp=%r15)
+movq %r9,72(%r15)
+
+# qhasm: *(uint64 *)(tp + 80) = tt2d2
+# asm 1: movq <tt2d2=int64#7,80(<tp=int64#13)
+# asm 2: movq <tt2d2=%rax,80(<tp=%r15)
+movq %rax,80(%r15)
+
+# qhasm: *(uint64 *)(tp + 88) = tt2d3
+# asm 1: movq <tt2d3=int64#8,88(<tp=int64#13)
+# asm 2: movq <tt2d3=%r10,88(<tp=%r15)
+movq %r10,88(%r15)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/consts.s b/ext/ed25519-amd64-asm/consts.s
new file mode 100644
index 00000000..c272383f
--- /dev/null
+++ b/ext/ed25519-amd64-asm/consts.s
@@ -0,0 +1,39 @@
+.data
+
+.globl crypto_sign_ed25519_amd64_64_121666
+.globl crypto_sign_ed25519_amd64_64_MU0
+.globl crypto_sign_ed25519_amd64_64_MU1
+.globl crypto_sign_ed25519_amd64_64_MU2
+.globl crypto_sign_ed25519_amd64_64_MU3
+.globl crypto_sign_ed25519_amd64_64_MU4
+.globl crypto_sign_ed25519_amd64_64_ORDER0
+.globl crypto_sign_ed25519_amd64_64_ORDER1
+.globl crypto_sign_ed25519_amd64_64_ORDER2
+.globl crypto_sign_ed25519_amd64_64_ORDER3
+.globl crypto_sign_ed25519_amd64_64_EC2D0
+.globl crypto_sign_ed25519_amd64_64_EC2D1
+.globl crypto_sign_ed25519_amd64_64_EC2D2
+.globl crypto_sign_ed25519_amd64_64_EC2D3
+.globl crypto_sign_ed25519_amd64_64_38
+
+.p2align 4
+
+crypto_sign_ed25519_amd64_64_121666: .quad 121666
+
+crypto_sign_ed25519_amd64_64_MU0: .quad 0xED9CE5A30A2C131B
+crypto_sign_ed25519_amd64_64_MU1: .quad 0x2106215D086329A7
+crypto_sign_ed25519_amd64_64_MU2: .quad 0xFFFFFFFFFFFFFFEB
+crypto_sign_ed25519_amd64_64_MU3: .quad 0xFFFFFFFFFFFFFFFF
+crypto_sign_ed25519_amd64_64_MU4: .quad 0x000000000000000F
+
+crypto_sign_ed25519_amd64_64_ORDER0: .quad 0x5812631A5CF5D3ED
+crypto_sign_ed25519_amd64_64_ORDER1: .quad 0x14DEF9DEA2F79CD6
+crypto_sign_ed25519_amd64_64_ORDER2: .quad 0x0000000000000000
+crypto_sign_ed25519_amd64_64_ORDER3: .quad 0x1000000000000000
+
+crypto_sign_ed25519_amd64_64_EC2D0: .quad 0xEBD69B9426B2F146
+crypto_sign_ed25519_amd64_64_EC2D1: .quad 0x00E0149A8283B156
+crypto_sign_ed25519_amd64_64_EC2D2: .quad 0x198E80F2EEF3D130
+crypto_sign_ed25519_amd64_64_EC2D3: .quad 0xA406D9DC56DFFCE7
+
+crypto_sign_ed25519_amd64_64_38: .quad 38
diff --git a/ext/ed25519-amd64-asm/fe25519.h b/ext/ed25519-amd64-asm/fe25519.h
new file mode 100644
index 00000000..33ffabbe
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519.h
@@ -0,0 +1,64 @@
+#ifndef FE25519_H
+#define FE25519_H
+
+#define fe25519 crypto_sign_ed25519_amd64_64_fe25519
+#define fe25519_freeze crypto_sign_ed25519_amd64_64_fe25519_freeze
+#define fe25519_unpack crypto_sign_ed25519_amd64_64_fe25519_unpack
+#define fe25519_pack crypto_sign_ed25519_amd64_64_fe25519_pack
+#define fe25519_iszero_vartime crypto_sign_ed25519_amd64_64_fe25519_iszero_vartime
+#define fe25519_iseq_vartime crypto_sign_ed25519_amd64_64_fe25519_iseq_vartime
+#define fe25519_cmov crypto_sign_ed25519_amd64_64_fe25519_cmov
+#define fe25519_setint crypto_sign_ed25519_amd64_64_fe25519_setint
+#define fe25519_neg crypto_sign_ed25519_amd64_64_fe25519_neg
+#define fe25519_getparity crypto_sign_ed25519_amd64_64_fe25519_getparity
+#define fe25519_add crypto_sign_ed25519_amd64_64_fe25519_add
+#define fe25519_sub crypto_sign_ed25519_amd64_64_fe25519_sub
+#define fe25519_mul crypto_sign_ed25519_amd64_64_fe25519_mul
+#define fe25519_mul121666 crypto_sign_ed25519_amd64_64_fe25519_mul121666
+#define fe25519_square crypto_sign_ed25519_amd64_64_fe25519_square
+#define fe25519_invert crypto_sign_ed25519_amd64_64_fe25519_invert
+#define fe25519_pow2523 crypto_sign_ed25519_amd64_64_fe25519_pow2523
+
+typedef struct
+{
+ unsigned long long v[4];
+}
+fe25519;
+
+void fe25519_freeze(fe25519 *r);
+
+void fe25519_unpack(fe25519 *r, const unsigned char x[32]);
+
+void fe25519_pack(unsigned char r[32], const fe25519 *x);
+
+void fe25519_cmov(fe25519 *r, const fe25519 *x, unsigned char b);
+
+void fe25519_cswap(fe25519 *r, fe25519 *x, unsigned char b);
+
+void fe25519_setint(fe25519 *r, unsigned int v);
+
+void fe25519_neg(fe25519 *r, const fe25519 *x);
+
+unsigned char fe25519_getparity(const fe25519 *x);
+
+int fe25519_iszero_vartime(const fe25519 *x);
+
+int fe25519_iseq_vartime(const fe25519 *x, const fe25519 *y);
+
+void fe25519_add(fe25519 *r, const fe25519 *x, const fe25519 *y);
+
+void fe25519_sub(fe25519 *r, const fe25519 *x, const fe25519 *y);
+
+void fe25519_mul(fe25519 *r, const fe25519 *x, const fe25519 *y);
+
+void fe25519_mul121666(fe25519 *r, const fe25519 *x);
+
+void fe25519_square(fe25519 *r, const fe25519 *x);
+
+void fe25519_pow(fe25519 *r, const fe25519 *x, const unsigned char *e);
+
+void fe25519_invert(fe25519 *r, const fe25519 *x);
+
+void fe25519_pow2523(fe25519 *r, const fe25519 *x);
+
+#endif
diff --git a/ext/ed25519-amd64-asm/fe25519_add.s b/ext/ed25519-amd64-asm/fe25519_add.s
new file mode 100644
index 00000000..b2e56252
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_add.s
@@ -0,0 +1,189 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 xp
+
+# qhasm: int64 yp
+
+# qhasm: input rp
+
+# qhasm: input xp
+
+# qhasm: input yp
+
+# qhasm: int64 r0
+
+# qhasm: int64 r1
+
+# qhasm: int64 r2
+
+# qhasm: int64 r3
+
+# qhasm: int64 addt0
+
+# qhasm: int64 addt1
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_fe25519_add
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_fe25519_add
+.globl crypto_sign_ed25519_amd64_64_fe25519_add
+_crypto_sign_ed25519_amd64_64_fe25519_add:
+crypto_sign_ed25519_amd64_64_fe25519_add:
+mov %rsp,%r11
+and $31,%r11
+add $0,%r11
+sub %r11,%rsp
+
+# qhasm: r0 = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#2),>r0=int64#4
+# asm 2: movq 0(<xp=%rsi),>r0=%rcx
+movq 0(%rsi),%rcx
+
+# qhasm: r1 = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>r1=int64#5
+# asm 2: movq 8(<xp=%rsi),>r1=%r8
+movq 8(%rsi),%r8
+
+# qhasm: r2 = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>r2=int64#6
+# asm 2: movq 16(<xp=%rsi),>r2=%r9
+movq 16(%rsi),%r9
+
+# qhasm: r3 = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>r3=int64#2
+# asm 2: movq 24(<xp=%rsi),>r3=%rsi
+movq 24(%rsi),%rsi
+
+# qhasm: carry? r0 += *(uint64 *)(yp + 0)
+# asm 1: addq 0(<yp=int64#3),<r0=int64#4
+# asm 2: addq 0(<yp=%rdx),<r0=%rcx
+addq 0(%rdx),%rcx
+
+# qhasm: carry? r1 += *(uint64 *)(yp + 8) + carry
+# asm 1: adcq 8(<yp=int64#3),<r1=int64#5
+# asm 2: adcq 8(<yp=%rdx),<r1=%r8
+adcq 8(%rdx),%r8
+
+# qhasm: carry? r2 += *(uint64 *)(yp + 16) + carry
+# asm 1: adcq 16(<yp=int64#3),<r2=int64#6
+# asm 2: adcq 16(<yp=%rdx),<r2=%r9
+adcq 16(%rdx),%r9
+
+# qhasm: carry? r3 += *(uint64 *)(yp + 24) + carry
+# asm 1: adcq 24(<yp=int64#3),<r3=int64#2
+# asm 2: adcq 24(<yp=%rdx),<r3=%rsi
+adcq 24(%rdx),%rsi
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#3
+# asm 2: mov $0,>addt0=%rdx
+mov $0,%rdx
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#7
+# asm 2: mov $38,>addt1=%rax
+mov $38,%rax
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#3,<addt1=int64#7
+# asm 2: cmovae <addt0=%rdx,<addt1=%rax
+cmovae %rdx,%rax
+
+# qhasm: carry? r0 += addt1
+# asm 1: add <addt1=int64#7,<r0=int64#4
+# asm 2: add <addt1=%rax,<r0=%rcx
+add %rax,%rcx
+
+# qhasm: carry? r1 += addt0 + carry
+# asm 1: adc <addt0=int64#3,<r1=int64#5
+# asm 2: adc <addt0=%rdx,<r1=%r8
+adc %rdx,%r8
+
+# qhasm: carry? r2 += addt0 + carry
+# asm 1: adc <addt0=int64#3,<r2=int64#6
+# asm 2: adc <addt0=%rdx,<r2=%r9
+adc %rdx,%r9
+
+# qhasm: carry? r3 += addt0 + carry
+# asm 1: adc <addt0=int64#3,<r3=int64#2
+# asm 2: adc <addt0=%rdx,<r3=%rsi
+adc %rdx,%rsi
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#7,<addt0=int64#3
+# asm 2: cmovc <addt1=%rax,<addt0=%rdx
+cmovc %rax,%rdx
+
+# qhasm: r0 += addt0
+# asm 1: add <addt0=int64#3,<r0=int64#4
+# asm 2: add <addt0=%rdx,<r0=%rcx
+add %rdx,%rcx
+
+# qhasm: *(uint64 *)(rp + 0) = r0
+# asm 1: movq <r0=int64#4,0(<rp=int64#1)
+# asm 2: movq <r0=%rcx,0(<rp=%rdi)
+movq %rcx,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = r1
+# asm 1: movq <r1=int64#5,8(<rp=int64#1)
+# asm 2: movq <r1=%r8,8(<rp=%rdi)
+movq %r8,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = r2
+# asm 1: movq <r2=int64#6,16(<rp=int64#1)
+# asm 2: movq <r2=%r9,16(<rp=%rdi)
+movq %r9,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = r3
+# asm 1: movq <r3=int64#2,24(<rp=int64#1)
+# asm 2: movq <r3=%rsi,24(<rp=%rdi)
+movq %rsi,24(%rdi)
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/fe25519_freeze.s b/ext/ed25519-amd64-asm/fe25519_freeze.s
new file mode 100644
index 00000000..dea29021
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_freeze.s
@@ -0,0 +1,322 @@
+
+# qhasm: int64 rp
+
+# qhasm: input rp
+
+# qhasm: int64 r0
+
+# qhasm: int64 r1
+
+# qhasm: int64 r2
+
+# qhasm: int64 r3
+
+# qhasm: int64 t0
+
+# qhasm: int64 t1
+
+# qhasm: int64 t2
+
+# qhasm: int64 t3
+
+# qhasm: int64 two63
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_fe25519_freeze
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_fe25519_freeze
+.globl crypto_sign_ed25519_amd64_64_fe25519_freeze
+_crypto_sign_ed25519_amd64_64_fe25519_freeze:
+crypto_sign_ed25519_amd64_64_fe25519_freeze:
+mov %rsp,%r11
+and $31,%r11
+add $64,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: r0 = *(uint64 *) (rp + 0)
+# asm 1: movq 0(<rp=int64#1),>r0=int64#2
+# asm 2: movq 0(<rp=%rdi),>r0=%rsi
+movq 0(%rdi),%rsi
+
+# qhasm: r1 = *(uint64 *) (rp + 8)
+# asm 1: movq 8(<rp=int64#1),>r1=int64#3
+# asm 2: movq 8(<rp=%rdi),>r1=%rdx
+movq 8(%rdi),%rdx
+
+# qhasm: r2 = *(uint64 *) (rp + 16)
+# asm 1: movq 16(<rp=int64#1),>r2=int64#4
+# asm 2: movq 16(<rp=%rdi),>r2=%rcx
+movq 16(%rdi),%rcx
+
+# qhasm: r3 = *(uint64 *) (rp + 24)
+# asm 1: movq 24(<rp=int64#1),>r3=int64#5
+# asm 2: movq 24(<rp=%rdi),>r3=%r8
+movq 24(%rdi),%r8
+
+# qhasm: t0 = r0
+# asm 1: mov <r0=int64#2,>t0=int64#6
+# asm 2: mov <r0=%rsi,>t0=%r9
+mov %rsi,%r9
+
+# qhasm: t1 = r1
+# asm 1: mov <r1=int64#3,>t1=int64#7
+# asm 2: mov <r1=%rdx,>t1=%rax
+mov %rdx,%rax
+
+# qhasm: t2 = r2
+# asm 1: mov <r2=int64#4,>t2=int64#8
+# asm 2: mov <r2=%rcx,>t2=%r10
+mov %rcx,%r10
+
+# qhasm: t3 = r3
+# asm 1: mov <r3=int64#5,>t3=int64#9
+# asm 2: mov <r3=%r8,>t3=%r11
+mov %r8,%r11
+
+# qhasm: two63 = 1
+# asm 1: mov $1,>two63=int64#10
+# asm 2: mov $1,>two63=%r12
+mov $1,%r12
+
+# qhasm: two63 <<= 63
+# asm 1: shl $63,<two63=int64#10
+# asm 2: shl $63,<two63=%r12
+shl $63,%r12
+
+# qhasm: carry? t0 += 19
+# asm 1: add $19,<t0=int64#6
+# asm 2: add $19,<t0=%r9
+add $19,%r9
+
+# qhasm: carry? t1 += 0 + carry
+# asm 1: adc $0,<t1=int64#7
+# asm 2: adc $0,<t1=%rax
+adc $0,%rax
+
+# qhasm: carry? t2 += 0 + carry
+# asm 1: adc $0,<t2=int64#8
+# asm 2: adc $0,<t2=%r10
+adc $0,%r10
+
+# qhasm: carry? t3 += two63 + carry
+# asm 1: adc <two63=int64#10,<t3=int64#9
+# asm 2: adc <two63=%r12,<t3=%r11
+adc %r12,%r11
+
+# qhasm: r0 = t0 if carry
+# asm 1: cmovc <t0=int64#6,<r0=int64#2
+# asm 2: cmovc <t0=%r9,<r0=%rsi
+cmovc %r9,%rsi
+
+# qhasm: r1 = t1 if carry
+# asm 1: cmovc <t1=int64#7,<r1=int64#3
+# asm 2: cmovc <t1=%rax,<r1=%rdx
+cmovc %rax,%rdx
+
+# qhasm: r2 = t2 if carry
+# asm 1: cmovc <t2=int64#8,<r2=int64#4
+# asm 2: cmovc <t2=%r10,<r2=%rcx
+cmovc %r10,%rcx
+
+# qhasm: r3 = t3 if carry
+# asm 1: cmovc <t3=int64#9,<r3=int64#5
+# asm 2: cmovc <t3=%r11,<r3=%r8
+cmovc %r11,%r8
+
+# qhasm: t0 = r0
+# asm 1: mov <r0=int64#2,>t0=int64#6
+# asm 2: mov <r0=%rsi,>t0=%r9
+mov %rsi,%r9
+
+# qhasm: t1 = r1
+# asm 1: mov <r1=int64#3,>t1=int64#7
+# asm 2: mov <r1=%rdx,>t1=%rax
+mov %rdx,%rax
+
+# qhasm: t2 = r2
+# asm 1: mov <r2=int64#4,>t2=int64#8
+# asm 2: mov <r2=%rcx,>t2=%r10
+mov %rcx,%r10
+
+# qhasm: t3 = r3
+# asm 1: mov <r3=int64#5,>t3=int64#9
+# asm 2: mov <r3=%r8,>t3=%r11
+mov %r8,%r11
+
+# qhasm: carry? t0 += 19
+# asm 1: add $19,<t0=int64#6
+# asm 2: add $19,<t0=%r9
+add $19,%r9
+
+# qhasm: carry? t1 += 0 + carry
+# asm 1: adc $0,<t1=int64#7
+# asm 2: adc $0,<t1=%rax
+adc $0,%rax
+
+# qhasm: carry? t2 += 0 + carry
+# asm 1: adc $0,<t2=int64#8
+# asm 2: adc $0,<t2=%r10
+adc $0,%r10
+
+# qhasm: carry? t3 += two63 + carry
+# asm 1: adc <two63=int64#10,<t3=int64#9
+# asm 2: adc <two63=%r12,<t3=%r11
+adc %r12,%r11
+
+# qhasm: r0 = t0 if carry
+# asm 1: cmovc <t0=int64#6,<r0=int64#2
+# asm 2: cmovc <t0=%r9,<r0=%rsi
+cmovc %r9,%rsi
+
+# qhasm: r1 = t1 if carry
+# asm 1: cmovc <t1=int64#7,<r1=int64#3
+# asm 2: cmovc <t1=%rax,<r1=%rdx
+cmovc %rax,%rdx
+
+# qhasm: r2 = t2 if carry
+# asm 1: cmovc <t2=int64#8,<r2=int64#4
+# asm 2: cmovc <t2=%r10,<r2=%rcx
+cmovc %r10,%rcx
+
+# qhasm: r3 = t3 if carry
+# asm 1: cmovc <t3=int64#9,<r3=int64#5
+# asm 2: cmovc <t3=%r11,<r3=%r8
+cmovc %r11,%r8
+
+# qhasm: *(uint64 *)(rp + 0) = r0
+# asm 1: movq <r0=int64#2,0(<rp=int64#1)
+# asm 2: movq <r0=%rsi,0(<rp=%rdi)
+movq %rsi,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = r1
+# asm 1: movq <r1=int64#3,8(<rp=int64#1)
+# asm 2: movq <r1=%rdx,8(<rp=%rdi)
+movq %rdx,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = r2
+# asm 1: movq <r2=int64#4,16(<rp=int64#1)
+# asm 2: movq <r2=%rcx,16(<rp=%rdi)
+movq %rcx,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = r3
+# asm 1: movq <r3=int64#5,24(<rp=int64#1)
+# asm 2: movq <r3=%r8,24(<rp=%rdi)
+movq %r8,24(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/fe25519_getparity.c b/ext/ed25519-amd64-asm/fe25519_getparity.c
new file mode 100644
index 00000000..a003ec8f
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_getparity.c
@@ -0,0 +1,8 @@
+#include "fe25519.h"
+
+unsigned char fe25519_getparity(const fe25519 *x)
+{
+ fe25519 t = *x;
+ fe25519_freeze(&t);
+ return (unsigned char)t.v[0] & 1;
+}
diff --git a/ext/ed25519-amd64-asm/fe25519_invert.c b/ext/ed25519-amd64-asm/fe25519_invert.c
new file mode 100644
index 00000000..a46d141f
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_invert.c
@@ -0,0 +1,60 @@
+#include "fe25519.h"
+
+void fe25519_invert(fe25519 *r, const fe25519 *x)
+{
+ fe25519 z2;
+ fe25519 z9;
+ fe25519 z11;
+ fe25519 z2_5_0;
+ fe25519 z2_10_0;
+ fe25519 z2_20_0;
+ fe25519 z2_50_0;
+ fe25519 z2_100_0;
+ fe25519 t;
+ int i;
+
+ /* 2 */ fe25519_square(&z2,x);
+ /* 4 */ fe25519_square(&t,&z2);
+ /* 8 */ fe25519_square(&t,&t);
+ /* 9 */ fe25519_mul(&z9,&t,x);
+ /* 11 */ fe25519_mul(&z11,&z9,&z2);
+ /* 22 */ fe25519_square(&t,&z11);
+ /* 2^5 - 2^0 = 31 */ fe25519_mul(&z2_5_0,&t,&z9);
+
+ /* 2^6 - 2^1 */ fe25519_square(&t,&z2_5_0);
+ /* 2^20 - 2^10 */ for (i = 1;i < 5;i++) { fe25519_square(&t,&t); }
+ /* 2^10 - 2^0 */ fe25519_mul(&z2_10_0,&t,&z2_5_0);
+
+ /* 2^11 - 2^1 */ fe25519_square(&t,&z2_10_0);
+ /* 2^20 - 2^10 */ for (i = 1;i < 10;i++) { fe25519_square(&t,&t); }
+ /* 2^20 - 2^0 */ fe25519_mul(&z2_20_0,&t,&z2_10_0);
+
+ /* 2^21 - 2^1 */ fe25519_square(&t,&z2_20_0);
+ /* 2^40 - 2^20 */ for (i = 1;i < 20;i++) { fe25519_square(&t,&t); }
+ /* 2^40 - 2^0 */ fe25519_mul(&t,&t,&z2_20_0);
+
+ /* 2^41 - 2^1 */ fe25519_square(&t,&t);
+ /* 2^50 - 2^10 */ for (i = 1;i < 10;i++) { fe25519_square(&t,&t); }
+ /* 2^50 - 2^0 */ fe25519_mul(&z2_50_0,&t,&z2_10_0);
+
+ /* 2^51 - 2^1 */ fe25519_square(&t,&z2_50_0);
+ /* 2^100 - 2^50 */ for (i = 1;i < 50;i++) { fe25519_square(&t,&t); }
+ /* 2^100 - 2^0 */ fe25519_mul(&z2_100_0,&t,&z2_50_0);
+
+ /* 2^101 - 2^1 */ fe25519_square(&t,&z2_100_0);
+ /* 2^200 - 2^100 */ for (i = 1;i < 100;i++) { fe25519_square(&t,&t); }
+ /* 2^200 - 2^0 */ fe25519_mul(&t,&t,&z2_100_0);
+
+ /* 2^201 - 2^1 */ fe25519_square(&t,&t);
+ /* 2^250 - 2^50 */ for (i = 1;i < 50;i++) { fe25519_square(&t,&t); }
+ /* 2^250 - 2^0 */ fe25519_mul(&t,&t,&z2_50_0);
+
+ /* 2^251 - 2^1 */ fe25519_square(&t,&t);
+ /* 2^252 - 2^2 */ fe25519_square(&t,&t);
+ /* 2^253 - 2^3 */ fe25519_square(&t,&t);
+
+ /* 2^254 - 2^4 */ fe25519_square(&t,&t);
+
+ /* 2^255 - 2^5 */ fe25519_square(&t,&t);
+ /* 2^255 - 21 */ fe25519_mul(r,&t,&z11);
+}
diff --git a/ext/ed25519-amd64-asm/fe25519_iseq.c b/ext/ed25519-amd64-asm/fe25519_iseq.c
new file mode 100644
index 00000000..bf72f8c9
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_iseq.c
@@ -0,0 +1,14 @@
+#include "fe25519.h"
+
+int fe25519_iseq_vartime(const fe25519 *x, const fe25519 *y)
+{
+ fe25519 t1 = *x;
+ fe25519 t2 = *y;
+ fe25519_freeze(&t1);
+ fe25519_freeze(&t2);
+ if(t1.v[0] != t2.v[0]) return 0;
+ if(t1.v[1] != t2.v[1]) return 0;
+ if(t1.v[2] != t2.v[2]) return 0;
+ if(t1.v[3] != t2.v[3]) return 0;
+ return 1;
+}
diff --git a/ext/ed25519-amd64-asm/fe25519_iszero.c b/ext/ed25519-amd64-asm/fe25519_iszero.c
new file mode 100644
index 00000000..99e4dafa
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_iszero.c
@@ -0,0 +1,12 @@
+#include "fe25519.h"
+
+int fe25519_iszero_vartime(const fe25519 *x)
+{
+ fe25519 t = *x;
+ fe25519_freeze(&t);
+ if (t.v[0]) return 0;
+ if (t.v[1]) return 0;
+ if (t.v[2]) return 0;
+ if (t.v[3]) return 0;
+ return 1;
+}
diff --git a/ext/ed25519-amd64-asm/fe25519_mul.s b/ext/ed25519-amd64-asm/fe25519_mul.s
new file mode 100644
index 00000000..7e24518d
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_mul.s
@@ -0,0 +1,865 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 xp
+
+# qhasm: int64 yp
+
+# qhasm: input rp
+
+# qhasm: input xp
+
+# qhasm: input yp
+
+# qhasm: int64 r0
+
+# qhasm: int64 r1
+
+# qhasm: int64 r2
+
+# qhasm: int64 r3
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: int64 mulr4
+
+# qhasm: int64 mulr5
+
+# qhasm: int64 mulr6
+
+# qhasm: int64 mulr7
+
+# qhasm: int64 mulr8
+
+# qhasm: int64 mulrax
+
+# qhasm: int64 mulrdx
+
+# qhasm: int64 mulx0
+
+# qhasm: int64 mulx1
+
+# qhasm: int64 mulx2
+
+# qhasm: int64 mulx3
+
+# qhasm: int64 mulc
+
+# qhasm: int64 mulzero
+
+# qhasm: int64 muli38
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_fe25519_mul
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_fe25519_mul
+.globl crypto_sign_ed25519_amd64_64_fe25519_mul
+_crypto_sign_ed25519_amd64_64_fe25519_mul:
+crypto_sign_ed25519_amd64_64_fe25519_mul:
+mov %rsp,%r11
+and $31,%r11
+add $64,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: yp = yp
+# asm 1: mov <yp=int64#3,>yp=int64#4
+# asm 2: mov <yp=%rdx,>yp=%rcx
+mov %rdx,%rcx
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#2),>mulx0=int64#10
+# asm 2: movq 0(<xp=%rsi),>mulx0=%r12
+movq 0(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(yp + 0)
+# asm 1: movq 0(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<yp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: r0 = mulrax
+# asm 1: mov <mulrax=int64#7,>r0=int64#11
+# asm 2: mov <mulrax=%rax,>r0=%r13
+mov %rax,%r13
+
+# qhasm: r1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>r1=int64#12
+# asm 2: mov <mulrdx=%rdx,>r1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(yp + 8)
+# asm 1: movq 8(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<yp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? r1 += mulrax
+# asm 1: add <mulrax=int64#7,<r1=int64#12
+# asm 2: add <mulrax=%rax,<r1=%r14
+add %rax,%r14
+
+# qhasm: r2 = 0
+# asm 1: mov $0,>r2=int64#13
+# asm 2: mov $0,>r2=%r15
+mov $0,%r15
+
+# qhasm: r2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<r2=int64#13
+# asm 2: adc <mulrdx=%rdx,<r2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(yp + 16)
+# asm 1: movq 16(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<yp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? r2 += mulrax
+# asm 1: add <mulrax=int64#7,<r2=int64#13
+# asm 2: add <mulrax=%rax,<r2=%r15
+add %rax,%r15
+
+# qhasm: r3 = 0
+# asm 1: mov $0,>r3=int64#14
+# asm 2: mov $0,>r3=%rbx
+mov $0,%rbx
+
+# qhasm: r3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<r3=int64#14
+# asm 2: adc <mulrdx=%rdx,<r3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(yp + 24)
+# asm 1: movq 24(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<yp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? r3 += mulrax
+# asm 1: add <mulrax=int64#7,<r3=int64#14
+# asm 2: add <mulrax=%rax,<r3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>mulx1=int64#10
+# asm 2: movq 8(<xp=%rsi),>mulx1=%r12
+movq 8(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(yp + 0)
+# asm 1: movq 0(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<yp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? r1 += mulrax
+# asm 1: add <mulrax=int64#7,<r1=int64#12
+# asm 2: add <mulrax=%rax,<r1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(yp + 8)
+# asm 1: movq 8(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<yp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? r2 += mulrax
+# asm 1: add <mulrax=int64#7,<r2=int64#13
+# asm 2: add <mulrax=%rax,<r2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? r2 += mulc
+# asm 1: add <mulc=int64#15,<r2=int64#13
+# asm 2: add <mulc=%rbp,<r2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(yp + 16)
+# asm 1: movq 16(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<yp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? r3 += mulrax
+# asm 1: add <mulrax=int64#7,<r3=int64#14
+# asm 2: add <mulrax=%rax,<r3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? r3 += mulc
+# asm 1: add <mulc=int64#15,<r3=int64#14
+# asm 2: add <mulc=%rbp,<r3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(yp + 24)
+# asm 1: movq 24(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<yp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>mulx2=int64#10
+# asm 2: movq 16(<xp=%rsi),>mulx2=%r12
+movq 16(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(yp + 0)
+# asm 1: movq 0(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<yp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? r2 += mulrax
+# asm 1: add <mulrax=int64#7,<r2=int64#13
+# asm 2: add <mulrax=%rax,<r2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(yp + 8)
+# asm 1: movq 8(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<yp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? r3 += mulrax
+# asm 1: add <mulrax=int64#7,<r3=int64#14
+# asm 2: add <mulrax=%rax,<r3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? r3 += mulc
+# asm 1: add <mulc=int64#15,<r3=int64#14
+# asm 2: add <mulc=%rbp,<r3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(yp + 16)
+# asm 1: movq 16(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<yp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(yp + 24)
+# asm 1: movq 24(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<yp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>mulx3=int64#2
+# asm 2: movq 24(<xp=%rsi),>mulx3=%rsi
+movq 24(%rsi),%rsi
+
+# qhasm: mulrax = *(uint64 *)(yp + 0)
+# asm 1: movq 0(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<yp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? r3 += mulrax
+# asm 1: add <mulrax=int64#7,<r3=int64#14
+# asm 2: add <mulrax=%rax,<r3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#10
+# asm 2: mov $0,>mulc=%r12
+mov $0,%r12
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#10
+# asm 2: adc <mulrdx=%rdx,<mulc=%r12
+adc %rdx,%r12
+
+# qhasm: mulrax = *(uint64 *)(yp + 8)
+# asm 1: movq 8(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<yp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#10,<mulr4=int64#5
+# asm 2: add <mulc=%r12,<mulr4=%r8
+add %r12,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#10
+# asm 2: mov $0,>mulc=%r12
+mov $0,%r12
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#10
+# asm 2: adc <mulrdx=%rdx,<mulc=%r12
+adc %rdx,%r12
+
+# qhasm: mulrax = *(uint64 *)(yp + 16)
+# asm 1: movq 16(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<yp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#10,<mulr5=int64#6
+# asm 2: add <mulc=%r12,<mulr5=%r9
+add %r12,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#10
+# asm 2: mov $0,>mulc=%r12
+mov $0,%r12
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#10
+# asm 2: adc <mulrdx=%rdx,<mulc=%r12
+adc %rdx,%r12
+
+# qhasm: mulrax = *(uint64 *)(yp + 24)
+# asm 1: movq 24(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<yp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#10,<mulr6=int64#8
+# asm 2: add <mulc=%r12,<mulr6=%r10
+add %r12,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#2
+# asm 2: mov <mulrax=%rax,>mulr4=%rsi
+mov %rax,%rsi
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#4
+# asm 2: mov <mulrdx=%rdx,>mulr5=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#6
+# asm 2: add <mulrax=%rax,<mulr7=%r9
+add %rax,%r9
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? r0 += mulr4
+# asm 1: add <mulr4=int64#2,<r0=int64#11
+# asm 2: add <mulr4=%rsi,<r0=%r13
+add %rsi,%r13
+
+# qhasm: carry? r1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#4,<r1=int64#12
+# asm 2: adc <mulr5=%rcx,<r1=%r14
+adc %rcx,%r14
+
+# qhasm: carry? r2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#5,<r2=int64#13
+# asm 2: adc <mulr6=%r8,<r2=%r15
+adc %r8,%r15
+
+# qhasm: carry? r3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#6,<r3=int64#14
+# asm 2: adc <mulr7=%r9,<r3=%rbx
+adc %r9,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#2
+# asm 2: mov $0,>mulzero=%rsi
+mov $0,%rsi
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulr8=int64#7
+# asm 2: adc <mulzero=%rsi,<mulr8=%rax
+adc %rsi,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#3
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? r0 += mulr8
+# asm 1: add <mulr8=int64#3,<r0=int64#11
+# asm 2: add <mulr8=%rdx,<r0=%r13
+add %rdx,%r13
+
+# qhasm: carry? r1 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<r1=int64#12
+# asm 2: adc <mulzero=%rsi,<r1=%r14
+adc %rsi,%r14
+
+# qhasm: carry? r2 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<r2=int64#13
+# asm 2: adc <mulzero=%rsi,<r2=%r15
+adc %rsi,%r15
+
+# qhasm: carry? r3 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<r3=int64#14
+# asm 2: adc <mulzero=%rsi,<r3=%rbx
+adc %rsi,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulzero=int64#2
+# asm 2: adc <mulzero=%rsi,<mulzero=%rsi
+adc %rsi,%rsi
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#2,>mulzero=int64#2
+# asm 2: imulq $38,<mulzero=%rsi,>mulzero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: r0 += mulzero
+# asm 1: add <mulzero=int64#2,<r0=int64#11
+# asm 2: add <mulzero=%rsi,<r0=%r13
+add %rsi,%r13
+
+# qhasm: *(uint64 *)(rp + 8) = r1
+# asm 1: movq <r1=int64#12,8(<rp=int64#1)
+# asm 2: movq <r1=%r14,8(<rp=%rdi)
+movq %r14,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = r2
+# asm 1: movq <r2=int64#13,16(<rp=int64#1)
+# asm 2: movq <r2=%r15,16(<rp=%rdi)
+movq %r15,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = r3
+# asm 1: movq <r3=int64#14,24(<rp=int64#1)
+# asm 2: movq <r3=%rbx,24(<rp=%rdi)
+movq %rbx,24(%rdi)
+
+# qhasm: *(uint64 *)(rp + 0) = r0
+# asm 1: movq <r0=int64#11,0(<rp=int64#1)
+# asm 2: movq <r0=%r13,0(<rp=%rdi)
+movq %r13,0(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/fe25519_neg.c b/ext/ed25519-amd64-asm/fe25519_neg.c
new file mode 100644
index 00000000..235b209d
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_neg.c
@@ -0,0 +1,8 @@
+#include "fe25519.h"
+
+void fe25519_neg(fe25519 *r, const fe25519 *x)
+{
+ fe25519 t;
+ fe25519_setint(&t,0);
+ fe25519_sub(r,&t,x);
+}
diff --git a/ext/ed25519-amd64-asm/fe25519_pack.c b/ext/ed25519-amd64-asm/fe25519_pack.c
new file mode 100644
index 00000000..caf51853
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_pack.c
@@ -0,0 +1,13 @@
+#include "fe25519.h"
+
+/* Assumes input x being reduced below 2^255 */
+void fe25519_pack(unsigned char r[32], const fe25519 *x)
+{
+ int i;
+ fe25519 t;
+ t = *x;
+ fe25519_freeze(&t);
+ /* assuming little-endian */
+ for(i=0;i<32;i++) r[i] = i[(unsigned char *)&t.v];
+}
+
diff --git a/ext/ed25519-amd64-asm/fe25519_pow2523.c b/ext/ed25519-amd64-asm/fe25519_pow2523.c
new file mode 100644
index 00000000..60042a0a
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_pow2523.c
@@ -0,0 +1,55 @@
+#include "fe25519.h"
+
+void fe25519_pow2523(fe25519 *r, const fe25519 *x)
+{
+ fe25519 z2;
+ fe25519 z9;
+ fe25519 z11;
+ fe25519 z2_5_0;
+ fe25519 z2_10_0;
+ fe25519 z2_20_0;
+ fe25519 z2_50_0;
+ fe25519 z2_100_0;
+ fe25519 t;
+ int i;
+
+ /* 2 */ fe25519_square(&z2,x);
+ /* 4 */ fe25519_square(&t,&z2);
+ /* 8 */ fe25519_square(&t,&t);
+ /* 9 */ fe25519_mul(&z9,&t,x);
+ /* 11 */ fe25519_mul(&z11,&z9,&z2);
+ /* 22 */ fe25519_square(&t,&z11);
+ /* 2^5 - 2^0 = 31 */ fe25519_mul(&z2_5_0,&t,&z9);
+
+ /* 2^6 - 2^1 */ fe25519_square(&t,&z2_5_0);
+ /* 2^10 - 2^5 */ for (i = 1;i < 5;i++) { fe25519_square(&t,&t); }
+ /* 2^10 - 2^0 */ fe25519_mul(&z2_10_0,&t,&z2_5_0);
+
+ /* 2^11 - 2^1 */ fe25519_square(&t,&z2_10_0);
+ /* 2^20 - 2^10 */ for (i = 1;i < 10;i++) { fe25519_square(&t,&t); }
+ /* 2^20 - 2^0 */ fe25519_mul(&z2_20_0,&t,&z2_10_0);
+
+ /* 2^21 - 2^1 */ fe25519_square(&t,&z2_20_0);
+ /* 2^40 - 2^20 */ for (i = 1;i < 20;i++) { fe25519_square(&t,&t); }
+ /* 2^40 - 2^0 */ fe25519_mul(&t,&t,&z2_20_0);
+
+ /* 2^41 - 2^1 */ fe25519_square(&t,&t);
+ /* 2^50 - 2^10 */ for (i = 1;i < 10;i++) { fe25519_square(&t,&t); }
+ /* 2^50 - 2^0 */ fe25519_mul(&z2_50_0,&t,&z2_10_0);
+
+ /* 2^51 - 2^1 */ fe25519_square(&t,&z2_50_0);
+ /* 2^100 - 2^50 */ for (i = 1;i < 50;i++) { fe25519_square(&t,&t); }
+ /* 2^100 - 2^0 */ fe25519_mul(&z2_100_0,&t,&z2_50_0);
+
+ /* 2^101 - 2^1 */ fe25519_square(&t,&z2_100_0);
+ /* 2^200 - 2^100 */ for (i = 1;i < 100;i++) { fe25519_square(&t,&t); }
+ /* 2^200 - 2^0 */ fe25519_mul(&t,&t,&z2_100_0);
+
+ /* 2^201 - 2^1 */ fe25519_square(&t,&t);
+ /* 2^250 - 2^50 */ for (i = 1;i < 50;i++) { fe25519_square(&t,&t); }
+ /* 2^250 - 2^0 */ fe25519_mul(&t,&t,&z2_50_0);
+
+ /* 2^251 - 2^1 */ fe25519_square(&t,&t);
+ /* 2^252 - 2^2 */ fe25519_square(&t,&t);
+ /* 2^252 - 3 */ fe25519_mul(r,&t,x);
+}
diff --git a/ext/ed25519-amd64-asm/fe25519_setint.c b/ext/ed25519-amd64-asm/fe25519_setint.c
new file mode 100644
index 00000000..585c4bdd
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_setint.c
@@ -0,0 +1,9 @@
+#include "fe25519.h"
+
+void fe25519_setint(fe25519 *r, unsigned int v)
+{
+ r->v[0] = v;
+ r->v[1] = 0;
+ r->v[2] = 0;
+ r->v[3] = 0;
+}
diff --git a/ext/ed25519-amd64-asm/fe25519_square.s b/ext/ed25519-amd64-asm/fe25519_square.s
new file mode 100644
index 00000000..3f51fd13
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_square.s
@@ -0,0 +1,639 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 xp
+
+# qhasm: input rp
+
+# qhasm: input xp
+
+# qhasm: int64 r0
+
+# qhasm: int64 r1
+
+# qhasm: int64 r2
+
+# qhasm: int64 r3
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: int64 squarer4
+
+# qhasm: int64 squarer5
+
+# qhasm: int64 squarer6
+
+# qhasm: int64 squarer7
+
+# qhasm: int64 squarer8
+
+# qhasm: int64 squarerax
+
+# qhasm: int64 squarerdx
+
+# qhasm: int64 squaret1
+
+# qhasm: int64 squaret2
+
+# qhasm: int64 squaret3
+
+# qhasm: int64 squarec
+
+# qhasm: int64 squarezero
+
+# qhasm: int64 squarei38
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_fe25519_square
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_fe25519_square
+.globl crypto_sign_ed25519_amd64_64_fe25519_square
+_crypto_sign_ed25519_amd64_64_fe25519_square:
+crypto_sign_ed25519_amd64_64_fe25519_square:
+mov %rsp,%r11
+and $31,%r11
+add $64,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: squarer7 = 0
+# asm 1: mov $0,>squarer7=int64#4
+# asm 2: mov $0,>squarer7=%rcx
+mov $0,%rcx
+
+# qhasm: squarerax = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>squarerax=int64#7
+# asm 2: movq 8(<xp=%rsi),>squarerax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(xp + 0)
+# asm 1: mulq 0(<xp=int64#2)
+# asm 2: mulq 0(<xp=%rsi)
+mulq 0(%rsi)
+
+# qhasm: r1 = squarerax
+# asm 1: mov <squarerax=int64#7,>r1=int64#5
+# asm 2: mov <squarerax=%rax,>r1=%r8
+mov %rax,%r8
+
+# qhasm: r2 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>r2=int64#6
+# asm 2: mov <squarerdx=%rdx,>r2=%r9
+mov %rdx,%r9
+
+# qhasm: squarerax = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>squarerax=int64#7
+# asm 2: movq 16(<xp=%rsi),>squarerax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(xp + 8)
+# asm 1: mulq 8(<xp=int64#2)
+# asm 2: mulq 8(<xp=%rsi)
+mulq 8(%rsi)
+
+# qhasm: r3 = squarerax
+# asm 1: mov <squarerax=int64#7,>r3=int64#8
+# asm 2: mov <squarerax=%rax,>r3=%r10
+mov %rax,%r10
+
+# qhasm: squarer4 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer4=int64#9
+# asm 2: mov <squarerdx=%rdx,>squarer4=%r11
+mov %rdx,%r11
+
+# qhasm: squarerax = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>squarerax=int64#7
+# asm 2: movq 24(<xp=%rsi),>squarerax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(xp + 16)
+# asm 1: mulq 16(<xp=int64#2)
+# asm 2: mulq 16(<xp=%rsi)
+mulq 16(%rsi)
+
+# qhasm: squarer5 = squarerax
+# asm 1: mov <squarerax=int64#7,>squarer5=int64#10
+# asm 2: mov <squarerax=%rax,>squarer5=%r12
+mov %rax,%r12
+
+# qhasm: squarer6 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer6=int64#11
+# asm 2: mov <squarerdx=%rdx,>squarer6=%r13
+mov %rdx,%r13
+
+# qhasm: squarerax = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>squarerax=int64#7
+# asm 2: movq 16(<xp=%rsi),>squarerax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(xp + 0)
+# asm 1: mulq 0(<xp=int64#2)
+# asm 2: mulq 0(<xp=%rsi)
+mulq 0(%rsi)
+
+# qhasm: carry? r2 += squarerax
+# asm 1: add <squarerax=int64#7,<r2=int64#6
+# asm 2: add <squarerax=%rax,<r2=%r9
+add %rax,%r9
+
+# qhasm: carry? r3 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<r3=int64#8
+# asm 2: adc <squarerdx=%rdx,<r3=%r10
+adc %rdx,%r10
+
+# qhasm: squarer4 += 0 + carry
+# asm 1: adc $0,<squarer4=int64#9
+# asm 2: adc $0,<squarer4=%r11
+adc $0,%r11
+
+# qhasm: squarerax = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>squarerax=int64#7
+# asm 2: movq 24(<xp=%rsi),>squarerax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(xp + 8)
+# asm 1: mulq 8(<xp=int64#2)
+# asm 2: mulq 8(<xp=%rsi)
+mulq 8(%rsi)
+
+# qhasm: carry? squarer4 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer4=int64#9
+# asm 2: add <squarerax=%rax,<squarer4=%r11
+add %rax,%r11
+
+# qhasm: carry? squarer5 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer5=int64#10
+# asm 2: adc <squarerdx=%rdx,<squarer5=%r12
+adc %rdx,%r12
+
+# qhasm: squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarerax = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>squarerax=int64#7
+# asm 2: movq 24(<xp=%rsi),>squarerax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(xp + 0)
+# asm 1: mulq 0(<xp=int64#2)
+# asm 2: mulq 0(<xp=%rsi)
+mulq 0(%rsi)
+
+# qhasm: carry? r3 += squarerax
+# asm 1: add <squarerax=int64#7,<r3=int64#8
+# asm 2: add <squarerax=%rax,<r3=%r10
+add %rax,%r10
+
+# qhasm: carry? squarer4 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer4=int64#9
+# asm 2: adc <squarerdx=%rdx,<squarer4=%r11
+adc %rdx,%r11
+
+# qhasm: carry? squarer5 += 0 + carry
+# asm 1: adc $0,<squarer5=int64#10
+# asm 2: adc $0,<squarer5=%r12
+adc $0,%r12
+
+# qhasm: carry? squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarer7 += 0 + carry
+# asm 1: adc $0,<squarer7=int64#4
+# asm 2: adc $0,<squarer7=%rcx
+adc $0,%rcx
+
+# qhasm: carry? r1 += r1
+# asm 1: add <r1=int64#5,<r1=int64#5
+# asm 2: add <r1=%r8,<r1=%r8
+add %r8,%r8
+
+# qhasm: carry? r2 += r2 + carry
+# asm 1: adc <r2=int64#6,<r2=int64#6
+# asm 2: adc <r2=%r9,<r2=%r9
+adc %r9,%r9
+
+# qhasm: carry? r3 += r3 + carry
+# asm 1: adc <r3=int64#8,<r3=int64#8
+# asm 2: adc <r3=%r10,<r3=%r10
+adc %r10,%r10
+
+# qhasm: carry? squarer4 += squarer4 + carry
+# asm 1: adc <squarer4=int64#9,<squarer4=int64#9
+# asm 2: adc <squarer4=%r11,<squarer4=%r11
+adc %r11,%r11
+
+# qhasm: carry? squarer5 += squarer5 + carry
+# asm 1: adc <squarer5=int64#10,<squarer5=int64#10
+# asm 2: adc <squarer5=%r12,<squarer5=%r12
+adc %r12,%r12
+
+# qhasm: carry? squarer6 += squarer6 + carry
+# asm 1: adc <squarer6=int64#11,<squarer6=int64#11
+# asm 2: adc <squarer6=%r13,<squarer6=%r13
+adc %r13,%r13
+
+# qhasm: squarer7 += squarer7 + carry
+# asm 1: adc <squarer7=int64#4,<squarer7=int64#4
+# asm 2: adc <squarer7=%rcx,<squarer7=%rcx
+adc %rcx,%rcx
+
+# qhasm: squarerax = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#2),>squarerax=int64#7
+# asm 2: movq 0(<xp=%rsi),>squarerax=%rax
+movq 0(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(xp + 0)
+# asm 1: mulq 0(<xp=int64#2)
+# asm 2: mulq 0(<xp=%rsi)
+mulq 0(%rsi)
+
+# qhasm: r0 = squarerax
+# asm 1: mov <squarerax=int64#7,>r0=int64#12
+# asm 2: mov <squarerax=%rax,>r0=%r14
+mov %rax,%r14
+
+# qhasm: squaret1 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squaret1=int64#13
+# asm 2: mov <squarerdx=%rdx,>squaret1=%r15
+mov %rdx,%r15
+
+# qhasm: squarerax = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>squarerax=int64#7
+# asm 2: movq 8(<xp=%rsi),>squarerax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(xp + 8)
+# asm 1: mulq 8(<xp=int64#2)
+# asm 2: mulq 8(<xp=%rsi)
+mulq 8(%rsi)
+
+# qhasm: squaret2 = squarerax
+# asm 1: mov <squarerax=int64#7,>squaret2=int64#14
+# asm 2: mov <squarerax=%rax,>squaret2=%rbx
+mov %rax,%rbx
+
+# qhasm: squaret3 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squaret3=int64#15
+# asm 2: mov <squarerdx=%rdx,>squaret3=%rbp
+mov %rdx,%rbp
+
+# qhasm: squarerax = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>squarerax=int64#7
+# asm 2: movq 16(<xp=%rsi),>squarerax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(xp + 16)
+# asm 1: mulq 16(<xp=int64#2)
+# asm 2: mulq 16(<xp=%rsi)
+mulq 16(%rsi)
+
+# qhasm: carry? r1 += squaret1
+# asm 1: add <squaret1=int64#13,<r1=int64#5
+# asm 2: add <squaret1=%r15,<r1=%r8
+add %r15,%r8
+
+# qhasm: carry? r2 += squaret2 + carry
+# asm 1: adc <squaret2=int64#14,<r2=int64#6
+# asm 2: adc <squaret2=%rbx,<r2=%r9
+adc %rbx,%r9
+
+# qhasm: carry? r3 += squaret3 + carry
+# asm 1: adc <squaret3=int64#15,<r3=int64#8
+# asm 2: adc <squaret3=%rbp,<r3=%r10
+adc %rbp,%r10
+
+# qhasm: carry? squarer4 += squarerax + carry
+# asm 1: adc <squarerax=int64#7,<squarer4=int64#9
+# asm 2: adc <squarerax=%rax,<squarer4=%r11
+adc %rax,%r11
+
+# qhasm: carry? squarer5 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer5=int64#10
+# asm 2: adc <squarerdx=%rdx,<squarer5=%r12
+adc %rdx,%r12
+
+# qhasm: carry? squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarer7 += 0 + carry
+# asm 1: adc $0,<squarer7=int64#4
+# asm 2: adc $0,<squarer7=%rcx
+adc $0,%rcx
+
+# qhasm: squarerax = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>squarerax=int64#7
+# asm 2: movq 24(<xp=%rsi),>squarerax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(xp + 24)
+# asm 1: mulq 24(<xp=int64#2)
+# asm 2: mulq 24(<xp=%rsi)
+mulq 24(%rsi)
+
+# qhasm: carry? squarer6 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer6=int64#11
+# asm 2: add <squarerax=%rax,<squarer6=%r13
+add %rax,%r13
+
+# qhasm: squarer7 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer7=int64#4
+# asm 2: adc <squarerdx=%rdx,<squarer7=%rcx
+adc %rdx,%rcx
+
+# qhasm: squarerax = squarer4
+# asm 1: mov <squarer4=int64#9,>squarerax=int64#7
+# asm 2: mov <squarer4=%r11,>squarerax=%rax
+mov %r11,%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: squarer4 = squarerax
+# asm 1: mov <squarerax=int64#7,>squarer4=int64#2
+# asm 2: mov <squarerax=%rax,>squarer4=%rsi
+mov %rax,%rsi
+
+# qhasm: squarerax = squarer5
+# asm 1: mov <squarer5=int64#10,>squarerax=int64#7
+# asm 2: mov <squarer5=%r12,>squarerax=%rax
+mov %r12,%rax
+
+# qhasm: squarer5 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer5=int64#9
+# asm 2: mov <squarerdx=%rdx,>squarer5=%r11
+mov %rdx,%r11
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? squarer5 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer5=int64#9
+# asm 2: add <squarerax=%rax,<squarer5=%r11
+add %rax,%r11
+
+# qhasm: squarerax = squarer6
+# asm 1: mov <squarer6=int64#11,>squarerax=int64#7
+# asm 2: mov <squarer6=%r13,>squarerax=%rax
+mov %r13,%rax
+
+# qhasm: squarer6 = 0
+# asm 1: mov $0,>squarer6=int64#10
+# asm 2: mov $0,>squarer6=%r12
+mov $0,%r12
+
+# qhasm: squarer6 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer6=int64#10
+# asm 2: adc <squarerdx=%rdx,<squarer6=%r12
+adc %rdx,%r12
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? squarer6 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer6=int64#10
+# asm 2: add <squarerax=%rax,<squarer6=%r12
+add %rax,%r12
+
+# qhasm: squarerax = squarer7
+# asm 1: mov <squarer7=int64#4,>squarerax=int64#7
+# asm 2: mov <squarer7=%rcx,>squarerax=%rax
+mov %rcx,%rax
+
+# qhasm: squarer7 = 0
+# asm 1: mov $0,>squarer7=int64#4
+# asm 2: mov $0,>squarer7=%rcx
+mov $0,%rcx
+
+# qhasm: squarer7 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer7=int64#4
+# asm 2: adc <squarerdx=%rdx,<squarer7=%rcx
+adc %rdx,%rcx
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? squarer7 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer7=int64#4
+# asm 2: add <squarerax=%rax,<squarer7=%rcx
+add %rax,%rcx
+
+# qhasm: squarer8 = 0
+# asm 1: mov $0,>squarer8=int64#7
+# asm 2: mov $0,>squarer8=%rax
+mov $0,%rax
+
+# qhasm: squarer8 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer8=int64#7
+# asm 2: adc <squarerdx=%rdx,<squarer8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? r0 += squarer4
+# asm 1: add <squarer4=int64#2,<r0=int64#12
+# asm 2: add <squarer4=%rsi,<r0=%r14
+add %rsi,%r14
+
+# qhasm: carry? r1 += squarer5 + carry
+# asm 1: adc <squarer5=int64#9,<r1=int64#5
+# asm 2: adc <squarer5=%r11,<r1=%r8
+adc %r11,%r8
+
+# qhasm: carry? r2 += squarer6 + carry
+# asm 1: adc <squarer6=int64#10,<r2=int64#6
+# asm 2: adc <squarer6=%r12,<r2=%r9
+adc %r12,%r9
+
+# qhasm: carry? r3 += squarer7 + carry
+# asm 1: adc <squarer7=int64#4,<r3=int64#8
+# asm 2: adc <squarer7=%rcx,<r3=%r10
+adc %rcx,%r10
+
+# qhasm: squarezero = 0
+# asm 1: mov $0,>squarezero=int64#2
+# asm 2: mov $0,>squarezero=%rsi
+mov $0,%rsi
+
+# qhasm: squarer8 += squarezero + carry
+# asm 1: adc <squarezero=int64#2,<squarer8=int64#7
+# asm 2: adc <squarezero=%rsi,<squarer8=%rax
+adc %rsi,%rax
+
+# qhasm: squarer8 *= 38
+# asm 1: imulq $38,<squarer8=int64#7,>squarer8=int64#3
+# asm 2: imulq $38,<squarer8=%rax,>squarer8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? r0 += squarer8
+# asm 1: add <squarer8=int64#3,<r0=int64#12
+# asm 2: add <squarer8=%rdx,<r0=%r14
+add %rdx,%r14
+
+# qhasm: carry? r1 += squarezero + carry
+# asm 1: adc <squarezero=int64#2,<r1=int64#5
+# asm 2: adc <squarezero=%rsi,<r1=%r8
+adc %rsi,%r8
+
+# qhasm: carry? r2 += squarezero + carry
+# asm 1: adc <squarezero=int64#2,<r2=int64#6
+# asm 2: adc <squarezero=%rsi,<r2=%r9
+adc %rsi,%r9
+
+# qhasm: carry? r3 += squarezero + carry
+# asm 1: adc <squarezero=int64#2,<r3=int64#8
+# asm 2: adc <squarezero=%rsi,<r3=%r10
+adc %rsi,%r10
+
+# qhasm: squarezero += squarezero + carry
+# asm 1: adc <squarezero=int64#2,<squarezero=int64#2
+# asm 2: adc <squarezero=%rsi,<squarezero=%rsi
+adc %rsi,%rsi
+
+# qhasm: squarezero *= 38
+# asm 1: imulq $38,<squarezero=int64#2,>squarezero=int64#2
+# asm 2: imulq $38,<squarezero=%rsi,>squarezero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: r0 += squarezero
+# asm 1: add <squarezero=int64#2,<r0=int64#12
+# asm 2: add <squarezero=%rsi,<r0=%r14
+add %rsi,%r14
+
+# qhasm: *(uint64 *)(rp + 8) = r1
+# asm 1: movq <r1=int64#5,8(<rp=int64#1)
+# asm 2: movq <r1=%r8,8(<rp=%rdi)
+movq %r8,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = r2
+# asm 1: movq <r2=int64#6,16(<rp=int64#1)
+# asm 2: movq <r2=%r9,16(<rp=%rdi)
+movq %r9,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = r3
+# asm 1: movq <r3=int64#8,24(<rp=int64#1)
+# asm 2: movq <r3=%r10,24(<rp=%rdi)
+movq %r10,24(%rdi)
+
+# qhasm: *(uint64 *)(rp + 0) = r0
+# asm 1: movq <r0=int64#12,0(<rp=int64#1)
+# asm 2: movq <r0=%r14,0(<rp=%rdi)
+movq %r14,0(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/fe25519_sub.s b/ext/ed25519-amd64-asm/fe25519_sub.s
new file mode 100644
index 00000000..0b395bce
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_sub.s
@@ -0,0 +1,189 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 xp
+
+# qhasm: int64 yp
+
+# qhasm: input rp
+
+# qhasm: input xp
+
+# qhasm: input yp
+
+# qhasm: int64 r0
+
+# qhasm: int64 r1
+
+# qhasm: int64 r2
+
+# qhasm: int64 r3
+
+# qhasm: int64 subt0
+
+# qhasm: int64 subt1
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_fe25519_sub
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_fe25519_sub
+.globl crypto_sign_ed25519_amd64_64_fe25519_sub
+_crypto_sign_ed25519_amd64_64_fe25519_sub:
+crypto_sign_ed25519_amd64_64_fe25519_sub:
+mov %rsp,%r11
+and $31,%r11
+add $0,%r11
+sub %r11,%rsp
+
+# qhasm: r0 = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#2),>r0=int64#4
+# asm 2: movq 0(<xp=%rsi),>r0=%rcx
+movq 0(%rsi),%rcx
+
+# qhasm: r1 = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>r1=int64#5
+# asm 2: movq 8(<xp=%rsi),>r1=%r8
+movq 8(%rsi),%r8
+
+# qhasm: r2 = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>r2=int64#6
+# asm 2: movq 16(<xp=%rsi),>r2=%r9
+movq 16(%rsi),%r9
+
+# qhasm: r3 = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>r3=int64#2
+# asm 2: movq 24(<xp=%rsi),>r3=%rsi
+movq 24(%rsi),%rsi
+
+# qhasm: carry? r0 -= *(uint64 *)(yp + 0)
+# asm 1: subq 0(<yp=int64#3),<r0=int64#4
+# asm 2: subq 0(<yp=%rdx),<r0=%rcx
+subq 0(%rdx),%rcx
+
+# qhasm: carry? r1 -= *(uint64 *)(yp + 8) - carry
+# asm 1: sbbq 8(<yp=int64#3),<r1=int64#5
+# asm 2: sbbq 8(<yp=%rdx),<r1=%r8
+sbbq 8(%rdx),%r8
+
+# qhasm: carry? r2 -= *(uint64 *)(yp + 16) - carry
+# asm 1: sbbq 16(<yp=int64#3),<r2=int64#6
+# asm 2: sbbq 16(<yp=%rdx),<r2=%r9
+sbbq 16(%rdx),%r9
+
+# qhasm: carry? r3 -= *(uint64 *)(yp + 24) - carry
+# asm 1: sbbq 24(<yp=int64#3),<r3=int64#2
+# asm 2: sbbq 24(<yp=%rdx),<r3=%rsi
+sbbq 24(%rdx),%rsi
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#3
+# asm 2: mov $0,>subt0=%rdx
+mov $0,%rdx
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#7
+# asm 2: mov $38,>subt1=%rax
+mov $38,%rax
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#3,<subt1=int64#7
+# asm 2: cmovae <subt0=%rdx,<subt1=%rax
+cmovae %rdx,%rax
+
+# qhasm: carry? r0 -= subt1
+# asm 1: sub <subt1=int64#7,<r0=int64#4
+# asm 2: sub <subt1=%rax,<r0=%rcx
+sub %rax,%rcx
+
+# qhasm: carry? r1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#3,<r1=int64#5
+# asm 2: sbb <subt0=%rdx,<r1=%r8
+sbb %rdx,%r8
+
+# qhasm: carry? r2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#3,<r2=int64#6
+# asm 2: sbb <subt0=%rdx,<r2=%r9
+sbb %rdx,%r9
+
+# qhasm: carry? r3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#3,<r3=int64#2
+# asm 2: sbb <subt0=%rdx,<r3=%rsi
+sbb %rdx,%rsi
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#7,<subt0=int64#3
+# asm 2: cmovc <subt1=%rax,<subt0=%rdx
+cmovc %rax,%rdx
+
+# qhasm: r0 -= subt0
+# asm 1: sub <subt0=int64#3,<r0=int64#4
+# asm 2: sub <subt0=%rdx,<r0=%rcx
+sub %rdx,%rcx
+
+# qhasm: *(uint64 *)(rp + 0) = r0
+# asm 1: movq <r0=int64#4,0(<rp=int64#1)
+# asm 2: movq <r0=%rcx,0(<rp=%rdi)
+movq %rcx,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = r1
+# asm 1: movq <r1=int64#5,8(<rp=int64#1)
+# asm 2: movq <r1=%r8,8(<rp=%rdi)
+movq %r8,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = r2
+# asm 1: movq <r2=int64#6,16(<rp=int64#1)
+# asm 2: movq <r2=%r9,16(<rp=%rdi)
+movq %r9,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = r3
+# asm 1: movq <r3=int64#2,24(<rp=int64#1)
+# asm 2: movq <r3=%rsi,24(<rp=%rdi)
+movq %rsi,24(%rdi)
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/fe25519_unpack.c b/ext/ed25519-amd64-asm/fe25519_unpack.c
new file mode 100644
index 00000000..b3b0f4d5
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_unpack.c
@@ -0,0 +1,11 @@
+#include "fe25519.h"
+
+void fe25519_unpack(fe25519 *r, const unsigned char x[32])
+{
+ /* assuming little-endian */
+ r->v[0] = *(unsigned long long *)x;
+ r->v[1] = *(((unsigned long long *)x)+1);
+ r->v[2] = *(((unsigned long long *)x)+2);
+ r->v[3] = *(((unsigned long long *)x)+3);
+ r->v[3] &= 0x7fffffffffffffffULL;
+}
diff --git a/ext/ed25519-amd64-asm/ge25519.h b/ext/ed25519-amd64-asm/ge25519.h
new file mode 100644
index 00000000..0b15136b
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519.h
@@ -0,0 +1,95 @@
+#ifndef GE25519_H
+#define GE25519_H
+
+#include "fe25519.h"
+#include "sc25519.h"
+
+#define ge25519 crypto_sign_ed25519_amd64_64_ge25519
+#define ge25519_base crypto_sign_ed25519_amd64_64_ge25519_base
+#define ge25519_unpackneg_vartime crypto_sign_ed25519_amd64_64_unpackneg_vartime
+#define ge25519_pack crypto_sign_ed25519_amd64_64_pack
+#define ge25519_isneutral_vartime crypto_sign_ed25519_amd64_64_isneutral_vartime
+#define ge25519_add crypto_sign_ed25519_amd64_64_ge25519_add
+#define ge25519_double crypto_sign_ed25519_amd64_64_ge25519_double
+#define ge25519_double_scalarmult_vartime crypto_sign_ed25519_amd64_64_double_scalarmult_vartime
+#define ge25519_multi_scalarmult_vartime crypto_sign_ed25519_amd64_64_ge25519_multi_scalarmult_vartime
+#define ge25519_scalarmult_base crypto_sign_ed25519_amd64_64_scalarmult_base
+#define ge25519_p1p1_to_p2 crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p2
+#define ge25519_p1p1_to_p3 crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p3
+#define ge25519_add_p1p1 crypto_sign_ed25519_amd64_64_ge25519_add_p1p1
+#define ge25519_dbl_p1p1 crypto_sign_ed25519_amd64_64_ge25519_dbl_p1p1
+#define choose_t crypto_sign_ed25519_amd64_64_choose_t
+#define ge25519_nielsadd2 crypto_sign_ed25519_amd64_64_ge25519_nielsadd2
+#define ge25519_nielsadd_p1p1 crypto_sign_ed25519_amd64_64_ge25519_nielsadd_p1p1
+#define ge25519_pnielsadd_p1p1 crypto_sign_ed25519_amd64_64_ge25519_pnielsadd_p1p1
+
+
+#define ge25519_p3 ge25519
+
+typedef struct
+{
+ fe25519 x;
+ fe25519 y;
+ fe25519 z;
+ fe25519 t;
+} ge25519;
+
+typedef struct
+{
+ fe25519 x;
+ fe25519 z;
+ fe25519 y;
+ fe25519 t;
+} ge25519_p1p1;
+
+typedef struct
+{
+ fe25519 x;
+ fe25519 y;
+ fe25519 z;
+} ge25519_p2;
+
+typedef struct
+{
+ fe25519 ysubx;
+ fe25519 xaddy;
+ fe25519 t2d;
+} ge25519_niels;
+
+typedef struct
+{
+ fe25519 ysubx;
+ fe25519 xaddy;
+ fe25519 z;
+ fe25519 t2d;
+} ge25519_pniels;
+
+extern void ge25519_p1p1_to_p2(ge25519_p2 *r, const ge25519_p1p1 *p);
+extern void ge25519_p1p1_to_p3(ge25519_p3 *r, const ge25519_p1p1 *p);
+extern void ge25519_add_p1p1(ge25519_p1p1 *r, const ge25519_p3 *p, const ge25519_p3 *q);
+extern void ge25519_dbl_p1p1(ge25519_p1p1 *r, const ge25519_p2 *p);
+extern void choose_t(ge25519_niels *t, unsigned long long pos, signed long long b, const ge25519_niels *base_multiples);
+extern void ge25519_nielsadd2(ge25519_p3 *r, const ge25519_niels *q);
+extern void ge25519_nielsadd_p1p1(ge25519_p1p1 *r, const ge25519_p3 *p, const ge25519_niels *q);
+extern void ge25519_pnielsadd_p1p1(ge25519_p1p1 *r, const ge25519_p3 *p, const ge25519_pniels *q);
+
+extern const ge25519 ge25519_base;
+
+extern int ge25519_unpackneg_vartime(ge25519 *r, const unsigned char p[32]);
+
+extern void ge25519_pack(unsigned char r[32], const ge25519 *p);
+
+extern int ge25519_isneutral_vartime(const ge25519 *p);
+
+extern void ge25519_add(ge25519 *r, const ge25519 *p, const ge25519 *q);
+
+extern void ge25519_double(ge25519 *r, const ge25519 *p);
+
+/* computes [s1]p1 + [s2]ge25519_base */
+extern void ge25519_double_scalarmult_vartime(ge25519 *r, const ge25519 *p1, const sc25519 *s1, const sc25519 *s2);
+
+extern void ge25519_multi_scalarmult_vartime(ge25519 *r, ge25519 *p, sc25519 *s, const unsigned long long npoints);
+
+extern void ge25519_scalarmult_base(ge25519 *r, const sc25519 *s);
+
+#endif
diff --git a/ext/ed25519-amd64-asm/ge25519_add.c b/ext/ed25519-amd64-asm/ge25519_add.c
new file mode 100644
index 00000000..c4d1c68a
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_add.c
@@ -0,0 +1,8 @@
+#include "ge25519.h"
+
+void ge25519_add(ge25519_p3 *r, const ge25519_p3 *p, const ge25519_p3 *q)
+{
+ ge25519_p1p1 grp1p1;
+ ge25519_add_p1p1(&grp1p1, p, q);
+ ge25519_p1p1_to_p3(r, &grp1p1);
+}
diff --git a/ext/ed25519-amd64-asm/ge25519_add_p1p1.s b/ext/ed25519-amd64-asm/ge25519_add_p1p1.s
new file mode 100644
index 00000000..9557e075
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_add_p1p1.s
@@ -0,0 +1,4554 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 pp
+
+# qhasm: int64 qp
+
+# qhasm: input rp
+
+# qhasm: input pp
+
+# qhasm: input qp
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: int64 a0
+
+# qhasm: int64 a1
+
+# qhasm: int64 a2
+
+# qhasm: int64 a3
+
+# qhasm: stack64 a0_stack
+
+# qhasm: stack64 a1_stack
+
+# qhasm: stack64 a2_stack
+
+# qhasm: stack64 a3_stack
+
+# qhasm: int64 b0
+
+# qhasm: int64 b1
+
+# qhasm: int64 b2
+
+# qhasm: int64 b3
+
+# qhasm: stack64 b0_stack
+
+# qhasm: stack64 b1_stack
+
+# qhasm: stack64 b2_stack
+
+# qhasm: stack64 b3_stack
+
+# qhasm: int64 c0
+
+# qhasm: int64 c1
+
+# qhasm: int64 c2
+
+# qhasm: int64 c3
+
+# qhasm: stack64 c0_stack
+
+# qhasm: stack64 c1_stack
+
+# qhasm: stack64 c2_stack
+
+# qhasm: stack64 c3_stack
+
+# qhasm: int64 d0
+
+# qhasm: int64 d1
+
+# qhasm: int64 d2
+
+# qhasm: int64 d3
+
+# qhasm: stack64 d0_stack
+
+# qhasm: stack64 d1_stack
+
+# qhasm: stack64 d2_stack
+
+# qhasm: stack64 d3_stack
+
+# qhasm: int64 t10
+
+# qhasm: int64 t11
+
+# qhasm: int64 t12
+
+# qhasm: int64 t13
+
+# qhasm: stack64 t10_stack
+
+# qhasm: stack64 t11_stack
+
+# qhasm: stack64 t12_stack
+
+# qhasm: stack64 t13_stack
+
+# qhasm: int64 t20
+
+# qhasm: int64 t21
+
+# qhasm: int64 t22
+
+# qhasm: int64 t23
+
+# qhasm: stack64 t20_stack
+
+# qhasm: stack64 t21_stack
+
+# qhasm: stack64 t22_stack
+
+# qhasm: stack64 t23_stack
+
+# qhasm: int64 rx0
+
+# qhasm: int64 rx1
+
+# qhasm: int64 rx2
+
+# qhasm: int64 rx3
+
+# qhasm: int64 ry0
+
+# qhasm: int64 ry1
+
+# qhasm: int64 ry2
+
+# qhasm: int64 ry3
+
+# qhasm: int64 rz0
+
+# qhasm: int64 rz1
+
+# qhasm: int64 rz2
+
+# qhasm: int64 rz3
+
+# qhasm: int64 rt0
+
+# qhasm: int64 rt1
+
+# qhasm: int64 rt2
+
+# qhasm: int64 rt3
+
+# qhasm: int64 x0
+
+# qhasm: int64 x1
+
+# qhasm: int64 x2
+
+# qhasm: int64 x3
+
+# qhasm: int64 mulr4
+
+# qhasm: int64 mulr5
+
+# qhasm: int64 mulr6
+
+# qhasm: int64 mulr7
+
+# qhasm: int64 mulr8
+
+# qhasm: int64 mulrax
+
+# qhasm: int64 mulrdx
+
+# qhasm: int64 mulx0
+
+# qhasm: int64 mulx1
+
+# qhasm: int64 mulx2
+
+# qhasm: int64 mulx3
+
+# qhasm: int64 mulc
+
+# qhasm: int64 mulzero
+
+# qhasm: int64 muli38
+
+# qhasm: int64 addt0
+
+# qhasm: int64 addt1
+
+# qhasm: int64 subt0
+
+# qhasm: int64 subt1
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_ge25519_add_p1p1
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_ge25519_add_p1p1
+.globl crypto_sign_ed25519_amd64_64_ge25519_add_p1p1
+_crypto_sign_ed25519_amd64_64_ge25519_add_p1p1:
+crypto_sign_ed25519_amd64_64_ge25519_add_p1p1:
+mov %rsp,%r11
+and $31,%r11
+add $192,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: qp = qp
+# asm 1: mov <qp=int64#3,>qp=int64#4
+# asm 2: mov <qp=%rdx,>qp=%rcx
+mov %rdx,%rcx
+
+# qhasm: a0 = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>a0=int64#3
+# asm 2: movq 32(<pp=%rsi),>a0=%rdx
+movq 32(%rsi),%rdx
+
+# qhasm: a1 = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>a1=int64#5
+# asm 2: movq 40(<pp=%rsi),>a1=%r8
+movq 40(%rsi),%r8
+
+# qhasm: a2 = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>a2=int64#6
+# asm 2: movq 48(<pp=%rsi),>a2=%r9
+movq 48(%rsi),%r9
+
+# qhasm: a3 = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>a3=int64#7
+# asm 2: movq 56(<pp=%rsi),>a3=%rax
+movq 56(%rsi),%rax
+
+# qhasm: b0 = a0
+# asm 1: mov <a0=int64#3,>b0=int64#8
+# asm 2: mov <a0=%rdx,>b0=%r10
+mov %rdx,%r10
+
+# qhasm: b1 = a1
+# asm 1: mov <a1=int64#5,>b1=int64#9
+# asm 2: mov <a1=%r8,>b1=%r11
+mov %r8,%r11
+
+# qhasm: b2 = a2
+# asm 1: mov <a2=int64#6,>b2=int64#10
+# asm 2: mov <a2=%r9,>b2=%r12
+mov %r9,%r12
+
+# qhasm: b3 = a3
+# asm 1: mov <a3=int64#7,>b3=int64#11
+# asm 2: mov <a3=%rax,>b3=%r13
+mov %rax,%r13
+
+# qhasm: carry? a0 -= *(uint64 *)(pp + 0)
+# asm 1: subq 0(<pp=int64#2),<a0=int64#3
+# asm 2: subq 0(<pp=%rsi),<a0=%rdx
+subq 0(%rsi),%rdx
+
+# qhasm: carry? a1 -= *(uint64 *)(pp + 8) - carry
+# asm 1: sbbq 8(<pp=int64#2),<a1=int64#5
+# asm 2: sbbq 8(<pp=%rsi),<a1=%r8
+sbbq 8(%rsi),%r8
+
+# qhasm: carry? a2 -= *(uint64 *)(pp + 16) - carry
+# asm 1: sbbq 16(<pp=int64#2),<a2=int64#6
+# asm 2: sbbq 16(<pp=%rsi),<a2=%r9
+sbbq 16(%rsi),%r9
+
+# qhasm: carry? a3 -= *(uint64 *)(pp + 24) - carry
+# asm 1: sbbq 24(<pp=int64#2),<a3=int64#7
+# asm 2: sbbq 24(<pp=%rsi),<a3=%rax
+sbbq 24(%rsi),%rax
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#12
+# asm 2: mov $0,>subt0=%r14
+mov $0,%r14
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#13
+# asm 2: mov $38,>subt1=%r15
+mov $38,%r15
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#12,<subt1=int64#13
+# asm 2: cmovae <subt0=%r14,<subt1=%r15
+cmovae %r14,%r15
+
+# qhasm: carry? a0 -= subt1
+# asm 1: sub <subt1=int64#13,<a0=int64#3
+# asm 2: sub <subt1=%r15,<a0=%rdx
+sub %r15,%rdx
+
+# qhasm: carry? a1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<a1=int64#5
+# asm 2: sbb <subt0=%r14,<a1=%r8
+sbb %r14,%r8
+
+# qhasm: carry? a2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<a2=int64#6
+# asm 2: sbb <subt0=%r14,<a2=%r9
+sbb %r14,%r9
+
+# qhasm: carry? a3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<a3=int64#7
+# asm 2: sbb <subt0=%r14,<a3=%rax
+sbb %r14,%rax
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#13,<subt0=int64#12
+# asm 2: cmovc <subt1=%r15,<subt0=%r14
+cmovc %r15,%r14
+
+# qhasm: a0 -= subt0
+# asm 1: sub <subt0=int64#12,<a0=int64#3
+# asm 2: sub <subt0=%r14,<a0=%rdx
+sub %r14,%rdx
+
+# qhasm: carry? b0 += *(uint64 *)(pp + 0)
+# asm 1: addq 0(<pp=int64#2),<b0=int64#8
+# asm 2: addq 0(<pp=%rsi),<b0=%r10
+addq 0(%rsi),%r10
+
+# qhasm: carry? b1 += *(uint64 *)(pp + 8) + carry
+# asm 1: adcq 8(<pp=int64#2),<b1=int64#9
+# asm 2: adcq 8(<pp=%rsi),<b1=%r11
+adcq 8(%rsi),%r11
+
+# qhasm: carry? b2 += *(uint64 *)(pp + 16) + carry
+# asm 1: adcq 16(<pp=int64#2),<b2=int64#10
+# asm 2: adcq 16(<pp=%rsi),<b2=%r12
+adcq 16(%rsi),%r12
+
+# qhasm: carry? b3 += *(uint64 *)(pp + 24) + carry
+# asm 1: adcq 24(<pp=int64#2),<b3=int64#11
+# asm 2: adcq 24(<pp=%rsi),<b3=%r13
+adcq 24(%rsi),%r13
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#12
+# asm 2: mov $0,>addt0=%r14
+mov $0,%r14
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#13
+# asm 2: mov $38,>addt1=%r15
+mov $38,%r15
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#12,<addt1=int64#13
+# asm 2: cmovae <addt0=%r14,<addt1=%r15
+cmovae %r14,%r15
+
+# qhasm: carry? b0 += addt1
+# asm 1: add <addt1=int64#13,<b0=int64#8
+# asm 2: add <addt1=%r15,<b0=%r10
+add %r15,%r10
+
+# qhasm: carry? b1 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<b1=int64#9
+# asm 2: adc <addt0=%r14,<b1=%r11
+adc %r14,%r11
+
+# qhasm: carry? b2 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<b2=int64#10
+# asm 2: adc <addt0=%r14,<b2=%r12
+adc %r14,%r12
+
+# qhasm: carry? b3 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<b3=int64#11
+# asm 2: adc <addt0=%r14,<b3=%r13
+adc %r14,%r13
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#13,<addt0=int64#12
+# asm 2: cmovc <addt1=%r15,<addt0=%r14
+cmovc %r15,%r14
+
+# qhasm: b0 += addt0
+# asm 1: add <addt0=int64#12,<b0=int64#8
+# asm 2: add <addt0=%r14,<b0=%r10
+add %r14,%r10
+
+# qhasm: a0_stack = a0
+# asm 1: movq <a0=int64#3,>a0_stack=stack64#8
+# asm 2: movq <a0=%rdx,>a0_stack=56(%rsp)
+movq %rdx,56(%rsp)
+
+# qhasm: a1_stack = a1
+# asm 1: movq <a1=int64#5,>a1_stack=stack64#9
+# asm 2: movq <a1=%r8,>a1_stack=64(%rsp)
+movq %r8,64(%rsp)
+
+# qhasm: a2_stack = a2
+# asm 1: movq <a2=int64#6,>a2_stack=stack64#10
+# asm 2: movq <a2=%r9,>a2_stack=72(%rsp)
+movq %r9,72(%rsp)
+
+# qhasm: a3_stack = a3
+# asm 1: movq <a3=int64#7,>a3_stack=stack64#11
+# asm 2: movq <a3=%rax,>a3_stack=80(%rsp)
+movq %rax,80(%rsp)
+
+# qhasm: b0_stack = b0
+# asm 1: movq <b0=int64#8,>b0_stack=stack64#12
+# asm 2: movq <b0=%r10,>b0_stack=88(%rsp)
+movq %r10,88(%rsp)
+
+# qhasm: b1_stack = b1
+# asm 1: movq <b1=int64#9,>b1_stack=stack64#13
+# asm 2: movq <b1=%r11,>b1_stack=96(%rsp)
+movq %r11,96(%rsp)
+
+# qhasm: b2_stack = b2
+# asm 1: movq <b2=int64#10,>b2_stack=stack64#14
+# asm 2: movq <b2=%r12,>b2_stack=104(%rsp)
+movq %r12,104(%rsp)
+
+# qhasm: b3_stack = b3
+# asm 1: movq <b3=int64#11,>b3_stack=stack64#15
+# asm 2: movq <b3=%r13,>b3_stack=112(%rsp)
+movq %r13,112(%rsp)
+
+# qhasm: t10 = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#4),>t10=int64#3
+# asm 2: movq 32(<qp=%rcx),>t10=%rdx
+movq 32(%rcx),%rdx
+
+# qhasm: t11 = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#4),>t11=int64#5
+# asm 2: movq 40(<qp=%rcx),>t11=%r8
+movq 40(%rcx),%r8
+
+# qhasm: t12 = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#4),>t12=int64#6
+# asm 2: movq 48(<qp=%rcx),>t12=%r9
+movq 48(%rcx),%r9
+
+# qhasm: t13 = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#4),>t13=int64#7
+# asm 2: movq 56(<qp=%rcx),>t13=%rax
+movq 56(%rcx),%rax
+
+# qhasm: t20 = t10
+# asm 1: mov <t10=int64#3,>t20=int64#8
+# asm 2: mov <t10=%rdx,>t20=%r10
+mov %rdx,%r10
+
+# qhasm: t21 = t11
+# asm 1: mov <t11=int64#5,>t21=int64#9
+# asm 2: mov <t11=%r8,>t21=%r11
+mov %r8,%r11
+
+# qhasm: t22 = t12
+# asm 1: mov <t12=int64#6,>t22=int64#10
+# asm 2: mov <t12=%r9,>t22=%r12
+mov %r9,%r12
+
+# qhasm: t23 = t13
+# asm 1: mov <t13=int64#7,>t23=int64#11
+# asm 2: mov <t13=%rax,>t23=%r13
+mov %rax,%r13
+
+# qhasm: carry? t10 -= *(uint64 *) (qp + 0)
+# asm 1: subq 0(<qp=int64#4),<t10=int64#3
+# asm 2: subq 0(<qp=%rcx),<t10=%rdx
+subq 0(%rcx),%rdx
+
+# qhasm: carry? t11 -= *(uint64 *) (qp + 8) - carry
+# asm 1: sbbq 8(<qp=int64#4),<t11=int64#5
+# asm 2: sbbq 8(<qp=%rcx),<t11=%r8
+sbbq 8(%rcx),%r8
+
+# qhasm: carry? t12 -= *(uint64 *) (qp + 16) - carry
+# asm 1: sbbq 16(<qp=int64#4),<t12=int64#6
+# asm 2: sbbq 16(<qp=%rcx),<t12=%r9
+sbbq 16(%rcx),%r9
+
+# qhasm: carry? t13 -= *(uint64 *) (qp + 24) - carry
+# asm 1: sbbq 24(<qp=int64#4),<t13=int64#7
+# asm 2: sbbq 24(<qp=%rcx),<t13=%rax
+sbbq 24(%rcx),%rax
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#12
+# asm 2: mov $0,>subt0=%r14
+mov $0,%r14
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#13
+# asm 2: mov $38,>subt1=%r15
+mov $38,%r15
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#12,<subt1=int64#13
+# asm 2: cmovae <subt0=%r14,<subt1=%r15
+cmovae %r14,%r15
+
+# qhasm: carry? t10 -= subt1
+# asm 1: sub <subt1=int64#13,<t10=int64#3
+# asm 2: sub <subt1=%r15,<t10=%rdx
+sub %r15,%rdx
+
+# qhasm: carry? t11 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<t11=int64#5
+# asm 2: sbb <subt0=%r14,<t11=%r8
+sbb %r14,%r8
+
+# qhasm: carry? t12 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<t12=int64#6
+# asm 2: sbb <subt0=%r14,<t12=%r9
+sbb %r14,%r9
+
+# qhasm: carry? t13 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<t13=int64#7
+# asm 2: sbb <subt0=%r14,<t13=%rax
+sbb %r14,%rax
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#13,<subt0=int64#12
+# asm 2: cmovc <subt1=%r15,<subt0=%r14
+cmovc %r15,%r14
+
+# qhasm: t10 -= subt0
+# asm 1: sub <subt0=int64#12,<t10=int64#3
+# asm 2: sub <subt0=%r14,<t10=%rdx
+sub %r14,%rdx
+
+# qhasm: carry? t20 += *(uint64 *) (qp + 0)
+# asm 1: addq 0(<qp=int64#4),<t20=int64#8
+# asm 2: addq 0(<qp=%rcx),<t20=%r10
+addq 0(%rcx),%r10
+
+# qhasm: carry? t21 += *(uint64 *) (qp + 8) + carry
+# asm 1: adcq 8(<qp=int64#4),<t21=int64#9
+# asm 2: adcq 8(<qp=%rcx),<t21=%r11
+adcq 8(%rcx),%r11
+
+# qhasm: carry? t22 += *(uint64 *) (qp + 16) + carry
+# asm 1: adcq 16(<qp=int64#4),<t22=int64#10
+# asm 2: adcq 16(<qp=%rcx),<t22=%r12
+adcq 16(%rcx),%r12
+
+# qhasm: carry? t23 += *(uint64 *) (qp + 24) + carry
+# asm 1: adcq 24(<qp=int64#4),<t23=int64#11
+# asm 2: adcq 24(<qp=%rcx),<t23=%r13
+adcq 24(%rcx),%r13
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#12
+# asm 2: mov $0,>addt0=%r14
+mov $0,%r14
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#13
+# asm 2: mov $38,>addt1=%r15
+mov $38,%r15
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#12,<addt1=int64#13
+# asm 2: cmovae <addt0=%r14,<addt1=%r15
+cmovae %r14,%r15
+
+# qhasm: carry? t20 += addt1
+# asm 1: add <addt1=int64#13,<t20=int64#8
+# asm 2: add <addt1=%r15,<t20=%r10
+add %r15,%r10
+
+# qhasm: carry? t21 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<t21=int64#9
+# asm 2: adc <addt0=%r14,<t21=%r11
+adc %r14,%r11
+
+# qhasm: carry? t22 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<t22=int64#10
+# asm 2: adc <addt0=%r14,<t22=%r12
+adc %r14,%r12
+
+# qhasm: carry? t23 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<t23=int64#11
+# asm 2: adc <addt0=%r14,<t23=%r13
+adc %r14,%r13
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#13,<addt0=int64#12
+# asm 2: cmovc <addt1=%r15,<addt0=%r14
+cmovc %r15,%r14
+
+# qhasm: t20 += addt0
+# asm 1: add <addt0=int64#12,<t20=int64#8
+# asm 2: add <addt0=%r14,<t20=%r10
+add %r14,%r10
+
+# qhasm: t10_stack = t10
+# asm 1: movq <t10=int64#3,>t10_stack=stack64#16
+# asm 2: movq <t10=%rdx,>t10_stack=120(%rsp)
+movq %rdx,120(%rsp)
+
+# qhasm: t11_stack = t11
+# asm 1: movq <t11=int64#5,>t11_stack=stack64#17
+# asm 2: movq <t11=%r8,>t11_stack=128(%rsp)
+movq %r8,128(%rsp)
+
+# qhasm: t12_stack = t12
+# asm 1: movq <t12=int64#6,>t12_stack=stack64#18
+# asm 2: movq <t12=%r9,>t12_stack=136(%rsp)
+movq %r9,136(%rsp)
+
+# qhasm: t13_stack = t13
+# asm 1: movq <t13=int64#7,>t13_stack=stack64#19
+# asm 2: movq <t13=%rax,>t13_stack=144(%rsp)
+movq %rax,144(%rsp)
+
+# qhasm: t20_stack = t20
+# asm 1: movq <t20=int64#8,>t20_stack=stack64#20
+# asm 2: movq <t20=%r10,>t20_stack=152(%rsp)
+movq %r10,152(%rsp)
+
+# qhasm: t21_stack = t21
+# asm 1: movq <t21=int64#9,>t21_stack=stack64#21
+# asm 2: movq <t21=%r11,>t21_stack=160(%rsp)
+movq %r11,160(%rsp)
+
+# qhasm: t22_stack = t22
+# asm 1: movq <t22=int64#10,>t22_stack=stack64#22
+# asm 2: movq <t22=%r12,>t22_stack=168(%rsp)
+movq %r12,168(%rsp)
+
+# qhasm: t23_stack = t23
+# asm 1: movq <t23=int64#11,>t23_stack=stack64#23
+# asm 2: movq <t23=%r13,>t23_stack=176(%rsp)
+movq %r13,176(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = a0_stack
+# asm 1: movq <a0_stack=stack64#8,>mulx0=int64#10
+# asm 2: movq <a0_stack=56(%rsp),>mulx0=%r12
+movq 56(%rsp),%r12
+
+# qhasm: mulrax = t10_stack
+# asm 1: movq <t10_stack=stack64#16,>mulrax=int64#7
+# asm 2: movq <t10_stack=120(%rsp),>mulrax=%rax
+movq 120(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: a0 = mulrax
+# asm 1: mov <mulrax=int64#7,>a0=int64#11
+# asm 2: mov <mulrax=%rax,>a0=%r13
+mov %rax,%r13
+
+# qhasm: a1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>a1=int64#12
+# asm 2: mov <mulrdx=%rdx,>a1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = t11_stack
+# asm 1: movq <t11_stack=stack64#17,>mulrax=int64#7
+# asm 2: movq <t11_stack=128(%rsp),>mulrax=%rax
+movq 128(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? a1 += mulrax
+# asm 1: add <mulrax=int64#7,<a1=int64#12
+# asm 2: add <mulrax=%rax,<a1=%r14
+add %rax,%r14
+
+# qhasm: a2 = 0
+# asm 1: mov $0,>a2=int64#13
+# asm 2: mov $0,>a2=%r15
+mov $0,%r15
+
+# qhasm: a2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<a2=int64#13
+# asm 2: adc <mulrdx=%rdx,<a2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = t12_stack
+# asm 1: movq <t12_stack=stack64#18,>mulrax=int64#7
+# asm 2: movq <t12_stack=136(%rsp),>mulrax=%rax
+movq 136(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#13
+# asm 2: add <mulrax=%rax,<a2=%r15
+add %rax,%r15
+
+# qhasm: a3 = 0
+# asm 1: mov $0,>a3=int64#14
+# asm 2: mov $0,>a3=%rbx
+mov $0,%rbx
+
+# qhasm: a3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<a3=int64#14
+# asm 2: adc <mulrdx=%rdx,<a3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = t13_stack
+# asm 1: movq <t13_stack=stack64#19,>mulrax=int64#7
+# asm 2: movq <t13_stack=144(%rsp),>mulrax=%rax
+movq 144(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = a1_stack
+# asm 1: movq <a1_stack=stack64#9,>mulx1=int64#10
+# asm 2: movq <a1_stack=64(%rsp),>mulx1=%r12
+movq 64(%rsp),%r12
+
+# qhasm: mulrax = t10_stack
+# asm 1: movq <t10_stack=stack64#16,>mulrax=int64#7
+# asm 2: movq <t10_stack=120(%rsp),>mulrax=%rax
+movq 120(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? a1 += mulrax
+# asm 1: add <mulrax=int64#7,<a1=int64#12
+# asm 2: add <mulrax=%rax,<a1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t11_stack
+# asm 1: movq <t11_stack=stack64#17,>mulrax=int64#7
+# asm 2: movq <t11_stack=128(%rsp),>mulrax=%rax
+movq 128(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#13
+# asm 2: add <mulrax=%rax,<a2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a2 += mulc
+# asm 1: add <mulc=int64#15,<a2=int64#13
+# asm 2: add <mulc=%rbp,<a2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t12_stack
+# asm 1: movq <t12_stack=stack64#18,>mulrax=int64#7
+# asm 2: movq <t12_stack=136(%rsp),>mulrax=%rax
+movq 136(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a3 += mulc
+# asm 1: add <mulc=int64#15,<a3=int64#14
+# asm 2: add <mulc=%rbp,<a3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t13_stack
+# asm 1: movq <t13_stack=stack64#19,>mulrax=int64#7
+# asm 2: movq <t13_stack=144(%rsp),>mulrax=%rax
+movq 144(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = a2_stack
+# asm 1: movq <a2_stack=stack64#10,>mulx2=int64#10
+# asm 2: movq <a2_stack=72(%rsp),>mulx2=%r12
+movq 72(%rsp),%r12
+
+# qhasm: mulrax = t10_stack
+# asm 1: movq <t10_stack=stack64#16,>mulrax=int64#7
+# asm 2: movq <t10_stack=120(%rsp),>mulrax=%rax
+movq 120(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#13
+# asm 2: add <mulrax=%rax,<a2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t11_stack
+# asm 1: movq <t11_stack=stack64#17,>mulrax=int64#7
+# asm 2: movq <t11_stack=128(%rsp),>mulrax=%rax
+movq 128(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a3 += mulc
+# asm 1: add <mulc=int64#15,<a3=int64#14
+# asm 2: add <mulc=%rbp,<a3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t12_stack
+# asm 1: movq <t12_stack=stack64#18,>mulrax=int64#7
+# asm 2: movq <t12_stack=136(%rsp),>mulrax=%rax
+movq 136(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t13_stack
+# asm 1: movq <t13_stack=stack64#19,>mulrax=int64#7
+# asm 2: movq <t13_stack=144(%rsp),>mulrax=%rax
+movq 144(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = a3_stack
+# asm 1: movq <a3_stack=stack64#11,>mulx3=int64#10
+# asm 2: movq <a3_stack=80(%rsp),>mulx3=%r12
+movq 80(%rsp),%r12
+
+# qhasm: mulrax = t10_stack
+# asm 1: movq <t10_stack=stack64#16,>mulrax=int64#7
+# asm 2: movq <t10_stack=120(%rsp),>mulrax=%rax
+movq 120(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t11_stack
+# asm 1: movq <t11_stack=stack64#17,>mulrax=int64#7
+# asm 2: movq <t11_stack=128(%rsp),>mulrax=%rax
+movq 128(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t12_stack
+# asm 1: movq <t12_stack=stack64#18,>mulrax=int64#7
+# asm 2: movq <t12_stack=136(%rsp),>mulrax=%rax
+movq 136(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t13_stack
+# asm 1: movq <t13_stack=stack64#19,>mulrax=int64#7
+# asm 2: movq <t13_stack=144(%rsp),>mulrax=%rax
+movq 144(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#15,<mulr6=int64#8
+# asm 2: add <mulc=%rbp,<mulr6=%r10
+add %rbp,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#5
+# asm 2: mov <mulrax=%rax,>mulr4=%r8
+mov %rax,%r8
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#6
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r9
+mov %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#9
+# asm 2: add <mulrax=%rax,<mulr7=%r11
+add %rax,%r11
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? a0 += mulr4
+# asm 1: add <mulr4=int64#5,<a0=int64#11
+# asm 2: add <mulr4=%r8,<a0=%r13
+add %r8,%r13
+
+# qhasm: carry? a1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#6,<a1=int64#12
+# asm 2: adc <mulr5=%r9,<a1=%r14
+adc %r9,%r14
+
+# qhasm: carry? a2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#8,<a2=int64#13
+# asm 2: adc <mulr6=%r10,<a2=%r15
+adc %r10,%r15
+
+# qhasm: carry? a3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#9,<a3=int64#14
+# asm 2: adc <mulr7=%r11,<a3=%rbx
+adc %r11,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#5
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%r8
+imulq $38,%rax,%r8
+
+# qhasm: carry? a0 += mulr8
+# asm 1: add <mulr8=int64#5,<a0=int64#11
+# asm 2: add <mulr8=%r8,<a0=%r13
+add %r8,%r13
+
+# qhasm: carry? a1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a1=int64#12
+# asm 2: adc <mulzero=%rdx,<a1=%r14
+adc %rdx,%r14
+
+# qhasm: carry? a2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a2=int64#13
+# asm 2: adc <mulzero=%rdx,<a2=%r15
+adc %rdx,%r15
+
+# qhasm: carry? a3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a3=int64#14
+# asm 2: adc <mulzero=%rdx,<a3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: a0 += mulzero
+# asm 1: add <mulzero=int64#3,<a0=int64#11
+# asm 2: add <mulzero=%rdx,<a0=%r13
+add %rdx,%r13
+
+# qhasm: a0_stack = a0
+# asm 1: movq <a0=int64#11,>a0_stack=stack64#8
+# asm 2: movq <a0=%r13,>a0_stack=56(%rsp)
+movq %r13,56(%rsp)
+
+# qhasm: a1_stack = a1
+# asm 1: movq <a1=int64#12,>a1_stack=stack64#9
+# asm 2: movq <a1=%r14,>a1_stack=64(%rsp)
+movq %r14,64(%rsp)
+
+# qhasm: a2_stack = a2
+# asm 1: movq <a2=int64#13,>a2_stack=stack64#10
+# asm 2: movq <a2=%r15,>a2_stack=72(%rsp)
+movq %r15,72(%rsp)
+
+# qhasm: a3_stack = a3
+# asm 1: movq <a3=int64#14,>a3_stack=stack64#11
+# asm 2: movq <a3=%rbx,>a3_stack=80(%rsp)
+movq %rbx,80(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = b0_stack
+# asm 1: movq <b0_stack=stack64#12,>mulx0=int64#10
+# asm 2: movq <b0_stack=88(%rsp),>mulx0=%r12
+movq 88(%rsp),%r12
+
+# qhasm: mulrax = t20_stack
+# asm 1: movq <t20_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <t20_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: rx0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rx0=int64#11
+# asm 2: mov <mulrax=%rax,>rx0=%r13
+mov %rax,%r13
+
+# qhasm: rx1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rx1=int64#12
+# asm 2: mov <mulrdx=%rdx,>rx1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = t21_stack
+# asm 1: movq <t21_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <t21_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rx1 += mulrax
+# asm 1: add <mulrax=int64#7,<rx1=int64#12
+# asm 2: add <mulrax=%rax,<rx1=%r14
+add %rax,%r14
+
+# qhasm: rx2 = 0
+# asm 1: mov $0,>rx2=int64#13
+# asm 2: mov $0,>rx2=%r15
+mov $0,%r15
+
+# qhasm: rx2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rx2=int64#13
+# asm 2: adc <mulrdx=%rdx,<rx2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = t22_stack
+# asm 1: movq <t22_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <t22_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#13
+# asm 2: add <mulrax=%rax,<rx2=%r15
+add %rax,%r15
+
+# qhasm: rx3 = 0
+# asm 1: mov $0,>rx3=int64#14
+# asm 2: mov $0,>rx3=%rbx
+mov $0,%rbx
+
+# qhasm: rx3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rx3=int64#14
+# asm 2: adc <mulrdx=%rdx,<rx3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = t23_stack
+# asm 1: movq <t23_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <t23_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#14
+# asm 2: add <mulrax=%rax,<rx3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = b1_stack
+# asm 1: movq <b1_stack=stack64#13,>mulx1=int64#10
+# asm 2: movq <b1_stack=96(%rsp),>mulx1=%r12
+movq 96(%rsp),%r12
+
+# qhasm: mulrax = t20_stack
+# asm 1: movq <t20_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <t20_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rx1 += mulrax
+# asm 1: add <mulrax=int64#7,<rx1=int64#12
+# asm 2: add <mulrax=%rax,<rx1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t21_stack
+# asm 1: movq <t21_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <t21_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#13
+# asm 2: add <mulrax=%rax,<rx2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx2 += mulc
+# asm 1: add <mulc=int64#15,<rx2=int64#13
+# asm 2: add <mulc=%rbp,<rx2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t22_stack
+# asm 1: movq <t22_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <t22_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#14
+# asm 2: add <mulrax=%rax,<rx3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx3 += mulc
+# asm 1: add <mulc=int64#15,<rx3=int64#14
+# asm 2: add <mulc=%rbp,<rx3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t23_stack
+# asm 1: movq <t23_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <t23_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = b2_stack
+# asm 1: movq <b2_stack=stack64#14,>mulx2=int64#10
+# asm 2: movq <b2_stack=104(%rsp),>mulx2=%r12
+movq 104(%rsp),%r12
+
+# qhasm: mulrax = t20_stack
+# asm 1: movq <t20_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <t20_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#13
+# asm 2: add <mulrax=%rax,<rx2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t21_stack
+# asm 1: movq <t21_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <t21_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#14
+# asm 2: add <mulrax=%rax,<rx3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx3 += mulc
+# asm 1: add <mulc=int64#15,<rx3=int64#14
+# asm 2: add <mulc=%rbp,<rx3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t22_stack
+# asm 1: movq <t22_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <t22_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t23_stack
+# asm 1: movq <t23_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <t23_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = b3_stack
+# asm 1: movq <b3_stack=stack64#15,>mulx3=int64#10
+# asm 2: movq <b3_stack=112(%rsp),>mulx3=%r12
+movq 112(%rsp),%r12
+
+# qhasm: mulrax = t20_stack
+# asm 1: movq <t20_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <t20_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#14
+# asm 2: add <mulrax=%rax,<rx3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t21_stack
+# asm 1: movq <t21_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <t21_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t22_stack
+# asm 1: movq <t22_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <t22_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t23_stack
+# asm 1: movq <t23_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <t23_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#15,<mulr6=int64#8
+# asm 2: add <mulc=%rbp,<mulr6=%r10
+add %rbp,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#5
+# asm 2: mov <mulrax=%rax,>mulr4=%r8
+mov %rax,%r8
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#6
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r9
+mov %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#9
+# asm 2: add <mulrax=%rax,<mulr7=%r11
+add %rax,%r11
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rx0 += mulr4
+# asm 1: add <mulr4=int64#5,<rx0=int64#11
+# asm 2: add <mulr4=%r8,<rx0=%r13
+add %r8,%r13
+
+# qhasm: carry? rx1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#6,<rx1=int64#12
+# asm 2: adc <mulr5=%r9,<rx1=%r14
+adc %r9,%r14
+
+# qhasm: carry? rx2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#8,<rx2=int64#13
+# asm 2: adc <mulr6=%r10,<rx2=%r15
+adc %r10,%r15
+
+# qhasm: carry? rx3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#9,<rx3=int64#14
+# asm 2: adc <mulr7=%r11,<rx3=%rbx
+adc %r11,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#5
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%r8
+imulq $38,%rax,%r8
+
+# qhasm: carry? rx0 += mulr8
+# asm 1: add <mulr8=int64#5,<rx0=int64#11
+# asm 2: add <mulr8=%r8,<rx0=%r13
+add %r8,%r13
+
+# qhasm: carry? rx1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx1=int64#12
+# asm 2: adc <mulzero=%rdx,<rx1=%r14
+adc %rdx,%r14
+
+# qhasm: carry? rx2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx2=int64#13
+# asm 2: adc <mulzero=%rdx,<rx2=%r15
+adc %rdx,%r15
+
+# qhasm: carry? rx3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx3=int64#14
+# asm 2: adc <mulzero=%rdx,<rx3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: rx0 += mulzero
+# asm 1: add <mulzero=int64#3,<rx0=int64#11
+# asm 2: add <mulzero=%rdx,<rx0=%r13
+add %rdx,%r13
+
+# qhasm: ry0 = rx0
+# asm 1: mov <rx0=int64#11,>ry0=int64#3
+# asm 2: mov <rx0=%r13,>ry0=%rdx
+mov %r13,%rdx
+
+# qhasm: ry1 = rx1
+# asm 1: mov <rx1=int64#12,>ry1=int64#5
+# asm 2: mov <rx1=%r14,>ry1=%r8
+mov %r14,%r8
+
+# qhasm: ry2 = rx2
+# asm 1: mov <rx2=int64#13,>ry2=int64#6
+# asm 2: mov <rx2=%r15,>ry2=%r9
+mov %r15,%r9
+
+# qhasm: ry3 = rx3
+# asm 1: mov <rx3=int64#14,>ry3=int64#7
+# asm 2: mov <rx3=%rbx,>ry3=%rax
+mov %rbx,%rax
+
+# qhasm: carry? ry0 += a0_stack
+# asm 1: addq <a0_stack=stack64#8,<ry0=int64#3
+# asm 2: addq <a0_stack=56(%rsp),<ry0=%rdx
+addq 56(%rsp),%rdx
+
+# qhasm: carry? ry1 += a1_stack + carry
+# asm 1: adcq <a1_stack=stack64#9,<ry1=int64#5
+# asm 2: adcq <a1_stack=64(%rsp),<ry1=%r8
+adcq 64(%rsp),%r8
+
+# qhasm: carry? ry2 += a2_stack + carry
+# asm 1: adcq <a2_stack=stack64#10,<ry2=int64#6
+# asm 2: adcq <a2_stack=72(%rsp),<ry2=%r9
+adcq 72(%rsp),%r9
+
+# qhasm: carry? ry3 += a3_stack + carry
+# asm 1: adcq <a3_stack=stack64#11,<ry3=int64#7
+# asm 2: adcq <a3_stack=80(%rsp),<ry3=%rax
+adcq 80(%rsp),%rax
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#8
+# asm 2: mov $0,>addt0=%r10
+mov $0,%r10
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#9
+# asm 2: mov $38,>addt1=%r11
+mov $38,%r11
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#8,<addt1=int64#9
+# asm 2: cmovae <addt0=%r10,<addt1=%r11
+cmovae %r10,%r11
+
+# qhasm: carry? ry0 += addt1
+# asm 1: add <addt1=int64#9,<ry0=int64#3
+# asm 2: add <addt1=%r11,<ry0=%rdx
+add %r11,%rdx
+
+# qhasm: carry? ry1 += addt0 + carry
+# asm 1: adc <addt0=int64#8,<ry1=int64#5
+# asm 2: adc <addt0=%r10,<ry1=%r8
+adc %r10,%r8
+
+# qhasm: carry? ry2 += addt0 + carry
+# asm 1: adc <addt0=int64#8,<ry2=int64#6
+# asm 2: adc <addt0=%r10,<ry2=%r9
+adc %r10,%r9
+
+# qhasm: carry? ry3 += addt0 + carry
+# asm 1: adc <addt0=int64#8,<ry3=int64#7
+# asm 2: adc <addt0=%r10,<ry3=%rax
+adc %r10,%rax
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#9,<addt0=int64#8
+# asm 2: cmovc <addt1=%r11,<addt0=%r10
+cmovc %r11,%r10
+
+# qhasm: ry0 += addt0
+# asm 1: add <addt0=int64#8,<ry0=int64#3
+# asm 2: add <addt0=%r10,<ry0=%rdx
+add %r10,%rdx
+
+# qhasm: carry? rx0 -= a0_stack
+# asm 1: subq <a0_stack=stack64#8,<rx0=int64#11
+# asm 2: subq <a0_stack=56(%rsp),<rx0=%r13
+subq 56(%rsp),%r13
+
+# qhasm: carry? rx1 -= a1_stack - carry
+# asm 1: sbbq <a1_stack=stack64#9,<rx1=int64#12
+# asm 2: sbbq <a1_stack=64(%rsp),<rx1=%r14
+sbbq 64(%rsp),%r14
+
+# qhasm: carry? rx2 -= a2_stack - carry
+# asm 1: sbbq <a2_stack=stack64#10,<rx2=int64#13
+# asm 2: sbbq <a2_stack=72(%rsp),<rx2=%r15
+sbbq 72(%rsp),%r15
+
+# qhasm: carry? rx3 -= a3_stack - carry
+# asm 1: sbbq <a3_stack=stack64#11,<rx3=int64#14
+# asm 2: sbbq <a3_stack=80(%rsp),<rx3=%rbx
+sbbq 80(%rsp),%rbx
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#8
+# asm 2: mov $0,>subt0=%r10
+mov $0,%r10
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#9
+# asm 2: mov $38,>subt1=%r11
+mov $38,%r11
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#8,<subt1=int64#9
+# asm 2: cmovae <subt0=%r10,<subt1=%r11
+cmovae %r10,%r11
+
+# qhasm: carry? rx0 -= subt1
+# asm 1: sub <subt1=int64#9,<rx0=int64#11
+# asm 2: sub <subt1=%r11,<rx0=%r13
+sub %r11,%r13
+
+# qhasm: carry? rx1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#8,<rx1=int64#12
+# asm 2: sbb <subt0=%r10,<rx1=%r14
+sbb %r10,%r14
+
+# qhasm: carry? rx2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#8,<rx2=int64#13
+# asm 2: sbb <subt0=%r10,<rx2=%r15
+sbb %r10,%r15
+
+# qhasm: carry? rx3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#8,<rx3=int64#14
+# asm 2: sbb <subt0=%r10,<rx3=%rbx
+sbb %r10,%rbx
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#9,<subt0=int64#8
+# asm 2: cmovc <subt1=%r11,<subt0=%r10
+cmovc %r11,%r10
+
+# qhasm: rx0 -= subt0
+# asm 1: sub <subt0=int64#8,<rx0=int64#11
+# asm 2: sub <subt0=%r10,<rx0=%r13
+sub %r10,%r13
+
+# qhasm: *(uint64 *) (rp + 0) = rx0
+# asm 1: movq <rx0=int64#11,0(<rp=int64#1)
+# asm 2: movq <rx0=%r13,0(<rp=%rdi)
+movq %r13,0(%rdi)
+
+# qhasm: *(uint64 *) (rp + 8) = rx1
+# asm 1: movq <rx1=int64#12,8(<rp=int64#1)
+# asm 2: movq <rx1=%r14,8(<rp=%rdi)
+movq %r14,8(%rdi)
+
+# qhasm: *(uint64 *) (rp + 16) = rx2
+# asm 1: movq <rx2=int64#13,16(<rp=int64#1)
+# asm 2: movq <rx2=%r15,16(<rp=%rdi)
+movq %r15,16(%rdi)
+
+# qhasm: *(uint64 *) (rp + 24) = rx3
+# asm 1: movq <rx3=int64#14,24(<rp=int64#1)
+# asm 2: movq <rx3=%rbx,24(<rp=%rdi)
+movq %rbx,24(%rdi)
+
+# qhasm: *(uint64 *) (rp + 64) = ry0
+# asm 1: movq <ry0=int64#3,64(<rp=int64#1)
+# asm 2: movq <ry0=%rdx,64(<rp=%rdi)
+movq %rdx,64(%rdi)
+
+# qhasm: *(uint64 *) (rp + 72) = ry1
+# asm 1: movq <ry1=int64#5,72(<rp=int64#1)
+# asm 2: movq <ry1=%r8,72(<rp=%rdi)
+movq %r8,72(%rdi)
+
+# qhasm: *(uint64 *) (rp + 80) = ry2
+# asm 1: movq <ry2=int64#6,80(<rp=int64#1)
+# asm 2: movq <ry2=%r9,80(<rp=%rdi)
+movq %r9,80(%rdi)
+
+# qhasm: *(uint64 *) (rp + 88) = ry3
+# asm 1: movq <ry3=int64#7,88(<rp=int64#1)
+# asm 2: movq <ry3=%rax,88(<rp=%rdi)
+movq %rax,88(%rdi)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulx0=int64#10
+# asm 2: movq 96(<pp=%rsi),>mulx0=%r12
+movq 96(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 96)
+# asm 1: movq 96(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 96(<qp=%rcx),>mulrax=%rax
+movq 96(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: c0 = mulrax
+# asm 1: mov <mulrax=int64#7,>c0=int64#11
+# asm 2: mov <mulrax=%rax,>c0=%r13
+mov %rax,%r13
+
+# qhasm: c1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>c1=int64#12
+# asm 2: mov <mulrdx=%rdx,>c1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 104)
+# asm 1: movq 104(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 104(<qp=%rcx),>mulrax=%rax
+movq 104(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c1 += mulrax
+# asm 1: add <mulrax=int64#7,<c1=int64#12
+# asm 2: add <mulrax=%rax,<c1=%r14
+add %rax,%r14
+
+# qhasm: c2 = 0
+# asm 1: mov $0,>c2=int64#13
+# asm 2: mov $0,>c2=%r15
+mov $0,%r15
+
+# qhasm: c2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<c2=int64#13
+# asm 2: adc <mulrdx=%rdx,<c2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 112)
+# asm 1: movq 112(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 112(<qp=%rcx),>mulrax=%rax
+movq 112(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: c3 = 0
+# asm 1: mov $0,>c3=int64#14
+# asm 2: mov $0,>c3=%rbx
+mov $0,%rbx
+
+# qhasm: c3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<c3=int64#14
+# asm 2: adc <mulrdx=%rdx,<c3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 120)
+# asm 1: movq 120(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 120(<qp=%rcx),>mulrax=%rax
+movq 120(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulx1=int64#10
+# asm 2: movq 104(<pp=%rsi),>mulx1=%r12
+movq 104(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 96)
+# asm 1: movq 96(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 96(<qp=%rcx),>mulrax=%rax
+movq 96(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c1 += mulrax
+# asm 1: add <mulrax=int64#7,<c1=int64#12
+# asm 2: add <mulrax=%rax,<c1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 104)
+# asm 1: movq 104(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 104(<qp=%rcx),>mulrax=%rax
+movq 104(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c2 += mulc
+# asm 1: add <mulc=int64#15,<c2=int64#13
+# asm 2: add <mulc=%rbp,<c2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 112)
+# asm 1: movq 112(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 112(<qp=%rcx),>mulrax=%rax
+movq 112(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c3 += mulc
+# asm 1: add <mulc=int64#15,<c3=int64#14
+# asm 2: add <mulc=%rbp,<c3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 120)
+# asm 1: movq 120(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 120(<qp=%rcx),>mulrax=%rax
+movq 120(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulx2=int64#10
+# asm 2: movq 112(<pp=%rsi),>mulx2=%r12
+movq 112(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 96)
+# asm 1: movq 96(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 96(<qp=%rcx),>mulrax=%rax
+movq 96(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 104)
+# asm 1: movq 104(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 104(<qp=%rcx),>mulrax=%rax
+movq 104(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c3 += mulc
+# asm 1: add <mulc=int64#15,<c3=int64#14
+# asm 2: add <mulc=%rbp,<c3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 112)
+# asm 1: movq 112(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 112(<qp=%rcx),>mulrax=%rax
+movq 112(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 120)
+# asm 1: movq 120(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 120(<qp=%rcx),>mulrax=%rax
+movq 120(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulx3=int64#10
+# asm 2: movq 120(<pp=%rsi),>mulx3=%r12
+movq 120(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 96)
+# asm 1: movq 96(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 96(<qp=%rcx),>mulrax=%rax
+movq 96(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 104)
+# asm 1: movq 104(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 104(<qp=%rcx),>mulrax=%rax
+movq 104(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 112)
+# asm 1: movq 112(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 112(<qp=%rcx),>mulrax=%rax
+movq 112(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 120)
+# asm 1: movq 120(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 120(<qp=%rcx),>mulrax=%rax
+movq 120(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#15,<mulr6=int64#8
+# asm 2: add <mulc=%rbp,<mulr6=%r10
+add %rbp,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#5
+# asm 2: mov <mulrax=%rax,>mulr4=%r8
+mov %rax,%r8
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#6
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r9
+mov %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#9
+# asm 2: add <mulrax=%rax,<mulr7=%r11
+add %rax,%r11
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? c0 += mulr4
+# asm 1: add <mulr4=int64#5,<c0=int64#11
+# asm 2: add <mulr4=%r8,<c0=%r13
+add %r8,%r13
+
+# qhasm: carry? c1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#6,<c1=int64#12
+# asm 2: adc <mulr5=%r9,<c1=%r14
+adc %r9,%r14
+
+# qhasm: carry? c2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#8,<c2=int64#13
+# asm 2: adc <mulr6=%r10,<c2=%r15
+adc %r10,%r15
+
+# qhasm: carry? c3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#9,<c3=int64#14
+# asm 2: adc <mulr7=%r11,<c3=%rbx
+adc %r11,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#5
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%r8
+imulq $38,%rax,%r8
+
+# qhasm: carry? c0 += mulr8
+# asm 1: add <mulr8=int64#5,<c0=int64#11
+# asm 2: add <mulr8=%r8,<c0=%r13
+add %r8,%r13
+
+# qhasm: carry? c1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c1=int64#12
+# asm 2: adc <mulzero=%rdx,<c1=%r14
+adc %rdx,%r14
+
+# qhasm: carry? c2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c2=int64#13
+# asm 2: adc <mulzero=%rdx,<c2=%r15
+adc %rdx,%r15
+
+# qhasm: carry? c3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c3=int64#14
+# asm 2: adc <mulzero=%rdx,<c3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: c0 += mulzero
+# asm 1: add <mulzero=int64#3,<c0=int64#11
+# asm 2: add <mulzero=%rdx,<c0=%r13
+add %rdx,%r13
+
+# qhasm: c0_stack = c0
+# asm 1: movq <c0=int64#11,>c0_stack=stack64#8
+# asm 2: movq <c0=%r13,>c0_stack=56(%rsp)
+movq %r13,56(%rsp)
+
+# qhasm: c1_stack = c1
+# asm 1: movq <c1=int64#12,>c1_stack=stack64#9
+# asm 2: movq <c1=%r14,>c1_stack=64(%rsp)
+movq %r14,64(%rsp)
+
+# qhasm: c2_stack = c2
+# asm 1: movq <c2=int64#13,>c2_stack=stack64#10
+# asm 2: movq <c2=%r15,>c2_stack=72(%rsp)
+movq %r15,72(%rsp)
+
+# qhasm: c3_stack = c3
+# asm 1: movq <c3=int64#14,>c3_stack=stack64#11
+# asm 2: movq <c3=%rbx,>c3_stack=80(%rsp)
+movq %rbx,80(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = c0_stack
+# asm 1: movq <c0_stack=stack64#8,>mulx0=int64#10
+# asm 2: movq <c0_stack=56(%rsp),>mulx0=%r12
+movq 56(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D0
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D0,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D0,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D0,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: c0 = mulrax
+# asm 1: mov <mulrax=int64#7,>c0=int64#11
+# asm 2: mov <mulrax=%rax,>c0=%r13
+mov %rax,%r13
+
+# qhasm: c1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>c1=int64#12
+# asm 2: mov <mulrdx=%rdx,>c1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D1
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D1,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D1,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D1,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c1 += mulrax
+# asm 1: add <mulrax=int64#7,<c1=int64#12
+# asm 2: add <mulrax=%rax,<c1=%r14
+add %rax,%r14
+
+# qhasm: c2 = 0
+# asm 1: mov $0,>c2=int64#13
+# asm 2: mov $0,>c2=%r15
+mov $0,%r15
+
+# qhasm: c2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<c2=int64#13
+# asm 2: adc <mulrdx=%rdx,<c2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D2
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D2,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D2,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D2,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: c3 = 0
+# asm 1: mov $0,>c3=int64#14
+# asm 2: mov $0,>c3=%rbx
+mov $0,%rbx
+
+# qhasm: c3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<c3=int64#14
+# asm 2: adc <mulrdx=%rdx,<c3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D3
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D3,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D3,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D3,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = c1_stack
+# asm 1: movq <c1_stack=stack64#9,>mulx1=int64#10
+# asm 2: movq <c1_stack=64(%rsp),>mulx1=%r12
+movq 64(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D0
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D0,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D0,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D0,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c1 += mulrax
+# asm 1: add <mulrax=int64#7,<c1=int64#12
+# asm 2: add <mulrax=%rax,<c1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D1
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D1,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D1,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D1,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c2 += mulc
+# asm 1: add <mulc=int64#15,<c2=int64#13
+# asm 2: add <mulc=%rbp,<c2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D2
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D2,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D2,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D2,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c3 += mulc
+# asm 1: add <mulc=int64#15,<c3=int64#14
+# asm 2: add <mulc=%rbp,<c3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D3
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D3,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D3,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D3,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = c2_stack
+# asm 1: movq <c2_stack=stack64#10,>mulx2=int64#10
+# asm 2: movq <c2_stack=72(%rsp),>mulx2=%r12
+movq 72(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D0
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D0,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D0,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D0,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D1
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D1,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D1,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D1,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c3 += mulc
+# asm 1: add <mulc=int64#15,<c3=int64#14
+# asm 2: add <mulc=%rbp,<c3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D2
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D2,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D2,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D2,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D3
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D3,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D3,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D3,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = c3_stack
+# asm 1: movq <c3_stack=stack64#11,>mulx3=int64#10
+# asm 2: movq <c3_stack=80(%rsp),>mulx3=%r12
+movq 80(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D0
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D0,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D0,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D0,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D1
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D1,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D1,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D1,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D2
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D2,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D2,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D2,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D3
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D3,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D3,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D3,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#15,<mulr6=int64#8
+# asm 2: add <mulc=%rbp,<mulr6=%r10
+add %rbp,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#5
+# asm 2: mov <mulrax=%rax,>mulr4=%r8
+mov %rax,%r8
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#6
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r9
+mov %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#9
+# asm 2: add <mulrax=%rax,<mulr7=%r11
+add %rax,%r11
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? c0 += mulr4
+# asm 1: add <mulr4=int64#5,<c0=int64#11
+# asm 2: add <mulr4=%r8,<c0=%r13
+add %r8,%r13
+
+# qhasm: carry? c1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#6,<c1=int64#12
+# asm 2: adc <mulr5=%r9,<c1=%r14
+adc %r9,%r14
+
+# qhasm: carry? c2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#8,<c2=int64#13
+# asm 2: adc <mulr6=%r10,<c2=%r15
+adc %r10,%r15
+
+# qhasm: carry? c3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#9,<c3=int64#14
+# asm 2: adc <mulr7=%r11,<c3=%rbx
+adc %r11,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#5
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%r8
+imulq $38,%rax,%r8
+
+# qhasm: carry? c0 += mulr8
+# asm 1: add <mulr8=int64#5,<c0=int64#11
+# asm 2: add <mulr8=%r8,<c0=%r13
+add %r8,%r13
+
+# qhasm: carry? c1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c1=int64#12
+# asm 2: adc <mulzero=%rdx,<c1=%r14
+adc %rdx,%r14
+
+# qhasm: carry? c2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c2=int64#13
+# asm 2: adc <mulzero=%rdx,<c2=%r15
+adc %rdx,%r15
+
+# qhasm: carry? c3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c3=int64#14
+# asm 2: adc <mulzero=%rdx,<c3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: c0 += mulzero
+# asm 1: add <mulzero=int64#3,<c0=int64#11
+# asm 2: add <mulzero=%rdx,<c0=%r13
+add %rdx,%r13
+
+# qhasm: c0_stack = c0
+# asm 1: movq <c0=int64#11,>c0_stack=stack64#8
+# asm 2: movq <c0=%r13,>c0_stack=56(%rsp)
+movq %r13,56(%rsp)
+
+# qhasm: c1_stack = c1
+# asm 1: movq <c1=int64#12,>c1_stack=stack64#9
+# asm 2: movq <c1=%r14,>c1_stack=64(%rsp)
+movq %r14,64(%rsp)
+
+# qhasm: c2_stack = c2
+# asm 1: movq <c2=int64#13,>c2_stack=stack64#10
+# asm 2: movq <c2=%r15,>c2_stack=72(%rsp)
+movq %r15,72(%rsp)
+
+# qhasm: c3_stack = c3
+# asm 1: movq <c3=int64#14,>c3_stack=stack64#11
+# asm 2: movq <c3=%rbx,>c3_stack=80(%rsp)
+movq %rbx,80(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = *(uint64 *)(pp + 64)
+# asm 1: movq 64(<pp=int64#2),>mulx0=int64#10
+# asm 2: movq 64(<pp=%rsi),>mulx0=%r12
+movq 64(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: rt0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rt0=int64#11
+# asm 2: mov <mulrax=%rax,>rt0=%r13
+mov %rax,%r13
+
+# qhasm: rt1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rt1=int64#12
+# asm 2: mov <mulrdx=%rdx,>rt1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rt1 += mulrax
+# asm 1: add <mulrax=int64#7,<rt1=int64#12
+# asm 2: add <mulrax=%rax,<rt1=%r14
+add %rax,%r14
+
+# qhasm: rt2 = 0
+# asm 1: mov $0,>rt2=int64#13
+# asm 2: mov $0,>rt2=%r15
+mov $0,%r15
+
+# qhasm: rt2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rt2=int64#13
+# asm 2: adc <mulrdx=%rdx,<rt2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#13
+# asm 2: add <mulrax=%rax,<rt2=%r15
+add %rax,%r15
+
+# qhasm: rt3 = 0
+# asm 1: mov $0,>rt3=int64#14
+# asm 2: mov $0,>rt3=%rbx
+mov $0,%rbx
+
+# qhasm: rt3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rt3=int64#14
+# asm 2: adc <mulrdx=%rdx,<rt3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#14
+# asm 2: add <mulrax=%rax,<rt3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = *(uint64 *)(pp + 72)
+# asm 1: movq 72(<pp=int64#2),>mulx1=int64#10
+# asm 2: movq 72(<pp=%rsi),>mulx1=%r12
+movq 72(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rt1 += mulrax
+# asm 1: add <mulrax=int64#7,<rt1=int64#12
+# asm 2: add <mulrax=%rax,<rt1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#13
+# asm 2: add <mulrax=%rax,<rt2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt2 += mulc
+# asm 1: add <mulc=int64#15,<rt2=int64#13
+# asm 2: add <mulc=%rbp,<rt2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#14
+# asm 2: add <mulrax=%rax,<rt3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt3 += mulc
+# asm 1: add <mulc=int64#15,<rt3=int64#14
+# asm 2: add <mulc=%rbp,<rt3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>mulx2=int64#10
+# asm 2: movq 80(<pp=%rsi),>mulx2=%r12
+movq 80(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#13
+# asm 2: add <mulrax=%rax,<rt2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#14
+# asm 2: add <mulrax=%rax,<rt3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt3 += mulc
+# asm 1: add <mulc=int64#15,<rt3=int64#14
+# asm 2: add <mulc=%rbp,<rt3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>mulx3=int64#2
+# asm 2: movq 88(<pp=%rsi),>mulx3=%rsi
+movq 88(%rsi),%rsi
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#14
+# asm 2: add <mulrax=%rax,<rt3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#10
+# asm 2: mov $0,>mulc=%r12
+mov $0,%r12
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#10
+# asm 2: adc <mulrdx=%rdx,<mulc=%r12
+adc %rdx,%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#10,<mulr4=int64#5
+# asm 2: add <mulc=%r12,<mulr4=%r8
+add %r12,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#10
+# asm 2: mov $0,>mulc=%r12
+mov $0,%r12
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#10
+# asm 2: adc <mulrdx=%rdx,<mulc=%r12
+adc %rdx,%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#10,<mulr5=int64#6
+# asm 2: add <mulc=%r12,<mulr5=%r9
+add %r12,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#10
+# asm 2: mov $0,>mulc=%r12
+mov $0,%r12
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#10
+# asm 2: adc <mulrdx=%rdx,<mulc=%r12
+adc %rdx,%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#10,<mulr6=int64#8
+# asm 2: add <mulc=%r12,<mulr6=%r10
+add %r12,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#2
+# asm 2: mov <mulrax=%rax,>mulr4=%rsi
+mov %rax,%rsi
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#4
+# asm 2: mov <mulrdx=%rdx,>mulr5=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#6
+# asm 2: add <mulrax=%rax,<mulr7=%r9
+add %rax,%r9
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rt0 += mulr4
+# asm 1: add <mulr4=int64#2,<rt0=int64#11
+# asm 2: add <mulr4=%rsi,<rt0=%r13
+add %rsi,%r13
+
+# qhasm: carry? rt1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#4,<rt1=int64#12
+# asm 2: adc <mulr5=%rcx,<rt1=%r14
+adc %rcx,%r14
+
+# qhasm: carry? rt2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#5,<rt2=int64#13
+# asm 2: adc <mulr6=%r8,<rt2=%r15
+adc %r8,%r15
+
+# qhasm: carry? rt3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#6,<rt3=int64#14
+# asm 2: adc <mulr7=%r9,<rt3=%rbx
+adc %r9,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#2
+# asm 2: mov $0,>mulzero=%rsi
+mov $0,%rsi
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulr8=int64#7
+# asm 2: adc <mulzero=%rsi,<mulr8=%rax
+adc %rsi,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#3
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? rt0 += mulr8
+# asm 1: add <mulr8=int64#3,<rt0=int64#11
+# asm 2: add <mulr8=%rdx,<rt0=%r13
+add %rdx,%r13
+
+# qhasm: carry? rt1 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt1=int64#12
+# asm 2: adc <mulzero=%rsi,<rt1=%r14
+adc %rsi,%r14
+
+# qhasm: carry? rt2 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt2=int64#13
+# asm 2: adc <mulzero=%rsi,<rt2=%r15
+adc %rsi,%r15
+
+# qhasm: carry? rt3 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt3=int64#14
+# asm 2: adc <mulzero=%rsi,<rt3=%rbx
+adc %rsi,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulzero=int64#2
+# asm 2: adc <mulzero=%rsi,<mulzero=%rsi
+adc %rsi,%rsi
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#2,>mulzero=int64#2
+# asm 2: imulq $38,<mulzero=%rsi,>mulzero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: rt0 += mulzero
+# asm 1: add <mulzero=int64#2,<rt0=int64#11
+# asm 2: add <mulzero=%rsi,<rt0=%r13
+add %rsi,%r13
+
+# qhasm: carry? rt0 += rt0
+# asm 1: add <rt0=int64#11,<rt0=int64#11
+# asm 2: add <rt0=%r13,<rt0=%r13
+add %r13,%r13
+
+# qhasm: carry? rt1 += rt1 + carry
+# asm 1: adc <rt1=int64#12,<rt1=int64#12
+# asm 2: adc <rt1=%r14,<rt1=%r14
+adc %r14,%r14
+
+# qhasm: carry? rt2 += rt2 + carry
+# asm 1: adc <rt2=int64#13,<rt2=int64#13
+# asm 2: adc <rt2=%r15,<rt2=%r15
+adc %r15,%r15
+
+# qhasm: carry? rt3 += rt3 + carry
+# asm 1: adc <rt3=int64#14,<rt3=int64#14
+# asm 2: adc <rt3=%rbx,<rt3=%rbx
+adc %rbx,%rbx
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#2
+# asm 2: mov $0,>addt0=%rsi
+mov $0,%rsi
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#3
+# asm 2: mov $38,>addt1=%rdx
+mov $38,%rdx
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#2,<addt1=int64#3
+# asm 2: cmovae <addt0=%rsi,<addt1=%rdx
+cmovae %rsi,%rdx
+
+# qhasm: carry? rt0 += addt1
+# asm 1: add <addt1=int64#3,<rt0=int64#11
+# asm 2: add <addt1=%rdx,<rt0=%r13
+add %rdx,%r13
+
+# qhasm: carry? rt1 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rt1=int64#12
+# asm 2: adc <addt0=%rsi,<rt1=%r14
+adc %rsi,%r14
+
+# qhasm: carry? rt2 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rt2=int64#13
+# asm 2: adc <addt0=%rsi,<rt2=%r15
+adc %rsi,%r15
+
+# qhasm: carry? rt3 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rt3=int64#14
+# asm 2: adc <addt0=%rsi,<rt3=%rbx
+adc %rsi,%rbx
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#3,<addt0=int64#2
+# asm 2: cmovc <addt1=%rdx,<addt0=%rsi
+cmovc %rdx,%rsi
+
+# qhasm: rt0 += addt0
+# asm 1: add <addt0=int64#2,<rt0=int64#11
+# asm 2: add <addt0=%rsi,<rt0=%r13
+add %rsi,%r13
+
+# qhasm: rz0 = rt0
+# asm 1: mov <rt0=int64#11,>rz0=int64#2
+# asm 2: mov <rt0=%r13,>rz0=%rsi
+mov %r13,%rsi
+
+# qhasm: rz1 = rt1
+# asm 1: mov <rt1=int64#12,>rz1=int64#3
+# asm 2: mov <rt1=%r14,>rz1=%rdx
+mov %r14,%rdx
+
+# qhasm: rz2 = rt2
+# asm 1: mov <rt2=int64#13,>rz2=int64#4
+# asm 2: mov <rt2=%r15,>rz2=%rcx
+mov %r15,%rcx
+
+# qhasm: rz3 = rt3
+# asm 1: mov <rt3=int64#14,>rz3=int64#5
+# asm 2: mov <rt3=%rbx,>rz3=%r8
+mov %rbx,%r8
+
+# qhasm: carry? rz0 += c0_stack
+# asm 1: addq <c0_stack=stack64#8,<rz0=int64#2
+# asm 2: addq <c0_stack=56(%rsp),<rz0=%rsi
+addq 56(%rsp),%rsi
+
+# qhasm: carry? rz1 += c1_stack + carry
+# asm 1: adcq <c1_stack=stack64#9,<rz1=int64#3
+# asm 2: adcq <c1_stack=64(%rsp),<rz1=%rdx
+adcq 64(%rsp),%rdx
+
+# qhasm: carry? rz2 += c2_stack + carry
+# asm 1: adcq <c2_stack=stack64#10,<rz2=int64#4
+# asm 2: adcq <c2_stack=72(%rsp),<rz2=%rcx
+adcq 72(%rsp),%rcx
+
+# qhasm: carry? rz3 += c3_stack + carry
+# asm 1: adcq <c3_stack=stack64#11,<rz3=int64#5
+# asm 2: adcq <c3_stack=80(%rsp),<rz3=%r8
+adcq 80(%rsp),%r8
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#6
+# asm 2: mov $0,>addt0=%r9
+mov $0,%r9
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#7
+# asm 2: mov $38,>addt1=%rax
+mov $38,%rax
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#6,<addt1=int64#7
+# asm 2: cmovae <addt0=%r9,<addt1=%rax
+cmovae %r9,%rax
+
+# qhasm: carry? rz0 += addt1
+# asm 1: add <addt1=int64#7,<rz0=int64#2
+# asm 2: add <addt1=%rax,<rz0=%rsi
+add %rax,%rsi
+
+# qhasm: carry? rz1 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<rz1=int64#3
+# asm 2: adc <addt0=%r9,<rz1=%rdx
+adc %r9,%rdx
+
+# qhasm: carry? rz2 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<rz2=int64#4
+# asm 2: adc <addt0=%r9,<rz2=%rcx
+adc %r9,%rcx
+
+# qhasm: carry? rz3 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<rz3=int64#5
+# asm 2: adc <addt0=%r9,<rz3=%r8
+adc %r9,%r8
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#7,<addt0=int64#6
+# asm 2: cmovc <addt1=%rax,<addt0=%r9
+cmovc %rax,%r9
+
+# qhasm: rz0 += addt0
+# asm 1: add <addt0=int64#6,<rz0=int64#2
+# asm 2: add <addt0=%r9,<rz0=%rsi
+add %r9,%rsi
+
+# qhasm: carry? rt0 -= c0_stack
+# asm 1: subq <c0_stack=stack64#8,<rt0=int64#11
+# asm 2: subq <c0_stack=56(%rsp),<rt0=%r13
+subq 56(%rsp),%r13
+
+# qhasm: carry? rt1 -= c1_stack - carry
+# asm 1: sbbq <c1_stack=stack64#9,<rt1=int64#12
+# asm 2: sbbq <c1_stack=64(%rsp),<rt1=%r14
+sbbq 64(%rsp),%r14
+
+# qhasm: carry? rt2 -= c2_stack - carry
+# asm 1: sbbq <c2_stack=stack64#10,<rt2=int64#13
+# asm 2: sbbq <c2_stack=72(%rsp),<rt2=%r15
+sbbq 72(%rsp),%r15
+
+# qhasm: carry? rt3 -= c3_stack - carry
+# asm 1: sbbq <c3_stack=stack64#11,<rt3=int64#14
+# asm 2: sbbq <c3_stack=80(%rsp),<rt3=%rbx
+sbbq 80(%rsp),%rbx
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#6
+# asm 2: mov $0,>subt0=%r9
+mov $0,%r9
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#7
+# asm 2: mov $38,>subt1=%rax
+mov $38,%rax
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#6,<subt1=int64#7
+# asm 2: cmovae <subt0=%r9,<subt1=%rax
+cmovae %r9,%rax
+
+# qhasm: carry? rt0 -= subt1
+# asm 1: sub <subt1=int64#7,<rt0=int64#11
+# asm 2: sub <subt1=%rax,<rt0=%r13
+sub %rax,%r13
+
+# qhasm: carry? rt1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#6,<rt1=int64#12
+# asm 2: sbb <subt0=%r9,<rt1=%r14
+sbb %r9,%r14
+
+# qhasm: carry? rt2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#6,<rt2=int64#13
+# asm 2: sbb <subt0=%r9,<rt2=%r15
+sbb %r9,%r15
+
+# qhasm: carry? rt3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#6,<rt3=int64#14
+# asm 2: sbb <subt0=%r9,<rt3=%rbx
+sbb %r9,%rbx
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#7,<subt0=int64#6
+# asm 2: cmovc <subt1=%rax,<subt0=%r9
+cmovc %rax,%r9
+
+# qhasm: rt0 -= subt0
+# asm 1: sub <subt0=int64#6,<rt0=int64#11
+# asm 2: sub <subt0=%r9,<rt0=%r13
+sub %r9,%r13
+
+# qhasm: *(uint64 *)(rp + 32) = rz0
+# asm 1: movq <rz0=int64#2,32(<rp=int64#1)
+# asm 2: movq <rz0=%rsi,32(<rp=%rdi)
+movq %rsi,32(%rdi)
+
+# qhasm: *(uint64 *)(rp + 40) = rz1
+# asm 1: movq <rz1=int64#3,40(<rp=int64#1)
+# asm 2: movq <rz1=%rdx,40(<rp=%rdi)
+movq %rdx,40(%rdi)
+
+# qhasm: *(uint64 *)(rp + 48) = rz2
+# asm 1: movq <rz2=int64#4,48(<rp=int64#1)
+# asm 2: movq <rz2=%rcx,48(<rp=%rdi)
+movq %rcx,48(%rdi)
+
+# qhasm: *(uint64 *)(rp + 56) = rz3
+# asm 1: movq <rz3=int64#5,56(<rp=int64#1)
+# asm 2: movq <rz3=%r8,56(<rp=%rdi)
+movq %r8,56(%rdi)
+
+# qhasm: *(uint64 *)(rp + 96) = rt0
+# asm 1: movq <rt0=int64#11,96(<rp=int64#1)
+# asm 2: movq <rt0=%r13,96(<rp=%rdi)
+movq %r13,96(%rdi)
+
+# qhasm: *(uint64 *)(rp + 104) = rt1
+# asm 1: movq <rt1=int64#12,104(<rp=int64#1)
+# asm 2: movq <rt1=%r14,104(<rp=%rdi)
+movq %r14,104(%rdi)
+
+# qhasm: *(uint64 *)(rp + 112) = rt2
+# asm 1: movq <rt2=int64#13,112(<rp=int64#1)
+# asm 2: movq <rt2=%r15,112(<rp=%rdi)
+movq %r15,112(%rdi)
+
+# qhasm: *(uint64 *)(rp + 120) = rt3
+# asm 1: movq <rt3=int64#14,120(<rp=int64#1)
+# asm 2: movq <rt3=%rbx,120(<rp=%rdi)
+movq %rbx,120(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/ge25519_base.c b/ext/ed25519-amd64-asm/ge25519_base.c
new file mode 100644
index 00000000..a7ae9786
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_base.c
@@ -0,0 +1,7 @@
+#include "ge25519.h"
+
+const ge25519 ge25519_base = {{{0xC9562D608F25D51A, 0x692CC7609525A7B2, 0xC0A4E231FDD6DC5C, 0x216936D3CD6E53FE}},
+ {{0x6666666666666658, 0x6666666666666666, 0x6666666666666666, 0x6666666666666666}},
+ {{0x0000000000000001, 0x0000000000000000, 0x0000000000000000, 000000000000000000}},
+ {{0x6DDE8AB3A5B7DDA3, 0x20F09F80775152F5, 0x66EA4E8E64ABE37D, 0x67875F0FD78B7665}}};
+
diff --git a/ext/ed25519-amd64-asm/ge25519_base_niels.data b/ext/ed25519-amd64-asm/ge25519_base_niels.data
new file mode 100644
index 00000000..8e3300cf
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_base_niels.data
@@ -0,0 +1,1536 @@
+{{{0x9d103905d740913e, 0xfd399f05d140beb3, 0xa5c18434688f8a09, 0x44fd2f9298f81267}},
+ {{0x2fbc93c6f58c3b85, 0xcf932dc6fb8c0e19, 0x270b4898643d42c2, 0x07cf9d3a33d4ba65}},
+ {{0xdbbd15674b6fbb59, 0x41e13f00eea2a5ea, 0xcdd49d1cc957c6fa, 0x4f0ebe1faf16ecca}}},
+{{{0x8a99a56042b4d5a8, 0x8f2b810c4e60acf6, 0xe09e236bb16e37aa, 0x6bb595a669c92555}},
+ {{0x9224e7fc933c71d7, 0x9f469d967a0ff5b5, 0x5aa69a65e1d60702, 0x590c063fa87d2e2e}},
+ {{0x6e347eaadad36802, 0xbaf3599383ee4805, 0x3bcabe10e6076826, 0x49314f0a165ed1b8}}},
+{{{0x56611fe8a4fcd265, 0x3bd353fde5c1ba7d, 0x8131f31a214bd6bd, 0x2ab91587555bda62}},
+ {{0xaf25b0a84cee9730, 0x025a8430e8864b8a, 0xc11b50029f016732, 0x7a164e1b9a80f8f4}},
+ {{0x9bf211f4f1674834, 0xb84e6b17f62df895, 0xd7de6f075b722a4e, 0x549a04b963bb2a21}}},
+{{{0x95fe050a056818bf, 0x327e89715660faa9, 0xc3e8e3cd06a05073, 0x27933f4c7445a49a}},
+ {{0x287351b98efc099f, 0x6765c6f47dfd2538, 0xca348d3dfb0a9265, 0x680e910321e58727}},
+ {{0xbf1e45ece51426b0, 0xe32bc63d6dba0f94, 0xe42974d58cf852c0, 0x44f079b1b0e64c18}}},
+{{{0x7f9182c3a447d6ba, 0xd50014d14b2729b7, 0xe33cf11cb864a087, 0x154a7e73eb1b55f3}},
+ {{0xa212bc4408a5bb33, 0x8d5048c3c75eed02, 0xdd1beb0c5abfec44, 0x2945ccf146e206eb}},
+ {{0xc832a179e7d003b3, 0x5f729d0a00124d7e, 0x62c1d4a10e6d8ff3, 0x68b8ac5938b27a98}}},
+{{{0x499806b67b7d8ca4, 0x575be28427d22739, 0xbb085ce7204553b9, 0x38b64c41ae417884}},
+ {{0x3a0ceeeb77157131, 0x9b27158900c8af88, 0x8065b668da59a736, 0x51e57bb6a2cc38bd}},
+ {{0x8f9dad91689de3a4, 0x175f2428f8fb9137, 0x050ab5329fcfb988, 0x7865dfa21354c09f}}},
+{{{0xba6f2c9aaa3221b1, 0x6ca021533bba23a7, 0x9dea764f92192c3a, 0x1d6edd5d2e5317e0}},
+ {{0x6b1a5cd0944ea3bf, 0x7470353ab39dc0d2, 0x71b2528228542e49, 0x461bea69283c927e}},
+ {{0x217a8aacab0fda36, 0xa528c6543d3549c8, 0x37d05b8b13ab7568, 0x233cef623a2cbc37}}},
+{{{0xe2a75dedf39234d9, 0x963d7680e1b558f9, 0x2c2741ac6e3c23fb, 0x3a9024a1320e01c3}},
+ {{0x59b7596604dd3e8f, 0x6cb30377e288702c, 0xb1339c665ed9c323, 0x0915e76061bce52f}},
+ {{0xdf7de835a834a37e, 0x8be19cda689857ea, 0x2c1185367167b326, 0x589eb3d9dbefd5c2}}},
+{{{0x7ec851ca553e2df3, 0xa71284cba64878b3, 0xe6b5e4193288d1e7, 0x4cf210ec5a9a8883}},
+ {{0x322d04a52d9021f6, 0xb9c19f3375c6bf9c, 0x587a3a4342d20b09, 0x143b1cf8aa64fe61}},
+ {{0x9f867c7d968acaab, 0x5f54258e27092729, 0xd0a7d34bea180975, 0x21b546a3374126e1}}},
+{{{0xa94ff858a2888343, 0xce0ed4565313ed3c, 0xf55c3dcfb5bf34fa, 0x0a653ca5c9eab371}},
+ {{0x490a7a45d185218f, 0x9a15377846049335, 0x0060ea09cc31e1f6, 0x7e041577f86ee965}},
+ {{0x66b2a496ce5b67f3, 0xff5492d8bd569796, 0x503cec294a592cd0, 0x566943650813acb2}}},
+{{{0xb818db0c26620798, 0x5d5c31d9606e354a, 0x0982fa4f00a8cdc7, 0x17e12bcd4653e2d4}},
+ {{0x5672f9eb1dabb69d, 0xba70b535afe853fc, 0x47ac0f752796d66d, 0x32a5351794117275}},
+ {{0xd3a644a6df648437, 0x703b6559880fbfdd, 0xcb852540ad3a1aa5, 0x0900b3f78e4c6468}}},
+{{{0x0a851b9f679d651b, 0xe108cb61033342f2, 0xd601f57fe88b30a3, 0x371f3acaed2dd714}},
+ {{0xed280fbec816ad31, 0x52d9595bd8e6efe3, 0x0fe71772f6c623f5, 0x4314030b051e293c}},
+ {{0xd560005efbf0bcad, 0x8eb70f2ed1870c5e, 0x201f9033d084e6a0, 0x4c3a5ae1ce7b6670}}},
+{{{0x4138a434dcb8fa95, 0x870cf67d6c96840b, 0xde388574297be82c, 0x7c814db27262a55a}},
+ {{0xbaf875e4c93da0dd, 0xb93282a771b9294d, 0x80d63fb7f4c6c460, 0x6de9c73dea66c181}},
+ {{0x478904d5a04df8f2, 0xfafbae4ab10142d3, 0xf6c8ac63555d0998, 0x5aac4a412f90b104}}},
+{{{0xc64f326b3ac92908, 0x5551b282e663e1e0, 0x476b35f54a1a4b83, 0x1b9da3fe189f68c2}},
+ {{0x603a0d0abd7f5134, 0x8089c932e1d3ae46, 0xdf2591398798bd63, 0x1c145cd274ba0235}},
+ {{0x32e8386475f3d743, 0x365b8baf6ae5d9ef, 0x825238b6385b681e, 0x234929c1167d65e1}}},
+{{{0x984decaba077ade8, 0x383f77ad19eb389d, 0xc7ec6b7e2954d794, 0x59c77b3aeb7c3a7a}},
+ {{0x48145cc21d099fcf, 0x4535c192cc28d7e5, 0x80e7c1e548247e01, 0x4a5f28743b2973ee}},
+ {{0xd3add725225ccf62, 0x911a3381b2152c5d, 0xd8b39fad5b08f87d, 0x6f05606b4799fe3b}}},
+{{{0x9ffe9e92177ba962, 0x98aee71d0de5cae1, 0x3ff4ae942d831044, 0x714de12e58533ac8}},
+ {{0x5b433149f91b6483, 0xadb5dc655a2cbf62, 0x87fa8412632827b3, 0x60895e91ab49f8d8}},
+ {{0xe9ecf2ed0cf86c18, 0xb46d06120735dfd4, 0xbc9da09804b96be7, 0x73e2e62fd96dc26b}}},
+{{{0xed5b635449aa515e, 0xa865c49f0bc6823a, 0x850c1fe95b42d1c4, 0x30d76d6f03d315b9}},
+ {{0x2eccdd0e632f9c1d, 0x51d0b69676893115, 0x52dfb76ba8637a58, 0x6dd37d49a00eef39}},
+ {{0x6c4444172106e4c7, 0xfb53d680928d7f69, 0xb4739ea4694d3f26, 0x10c697112e864bb0}}},
+{{{0x6493c4277dbe5fde, 0x265d4fad19ad7ea2, 0x0e00dfc846304590, 0x25e61cabed66fe09}},
+ {{0x0ca62aa08358c805, 0x6a3d4ae37a204247, 0x7464d3a63b11eddc, 0x03bf9baf550806ef}},
+ {{0x3f13e128cc586604, 0x6f5873ecb459747e, 0xa0b63dedcc1268f5, 0x566d78634586e22c}}},
+{{{0x1637a49f9cc10834, 0xbc8e56d5a89bc451, 0x1cb5ec0f7f7fd2db, 0x33975bca5ecc35d9}},
+ {{0xa1054285c65a2fd0, 0x6c64112af31667c3, 0x680ae240731aee58, 0x14fba5f34793b22a}},
+ {{0x3cd746166985f7d4, 0x593e5e84c9c80057, 0x2fc3f2b67b61131e, 0x14829cea83fc526c}}},
+{{{0xff437b8497dd95c2, 0x6c744e30aa4eb5a7, 0x9e0c5d613c85e88b, 0x2fd9c71e5f758173}},
+ {{0x21e70b2f4e71ecb8, 0xe656ddb940a477e3, 0xbf6556cece1d4f80, 0x05fc3bc4535d7b7e}},
+ {{0x24b8b3ae52afdedd, 0x3495638ced3b30cf, 0x33a4bc83a9be8195, 0x373767475c651f04}}},
+{{{0x2fba99fd40d1add9, 0xb307166f96f4d027, 0x4363f05215f03bae, 0x1fbea56c3b18f999}},
+ {{0x634095cb14246590, 0xef12144016c15535, 0x9e38140c8910bc60, 0x6bf5905730907c8c}},
+ {{0x0fa778f1e1415b8a, 0x06409ff7bac3a77e, 0x6f52d7b89aa29a50, 0x02521cf67a635a56}}},
+{{{0x513fee0b0a9d5294, 0x8f98e75c0fdf5a66, 0xd4618688bfe107ce, 0x3fa00a7e71382ced}},
+ {{0xb1146720772f5ee4, 0xe8f894b196079ace, 0x4af8224d00ac824a, 0x001753d9f7cd6cc4}},
+ {{0x3c69232d963ddb34, 0x1dde87dab4973858, 0xaad7d1f9a091f285, 0x12b5fe2fa048edb6}}},
+{{{0x71f0fbc496fce34d, 0x73b9826badf35bed, 0xd2047261ff28c561, 0x749b76f96fb1206f}},
+ {{0xdf2b7c26ad6f1e92, 0x4b66d323504b8913, 0x8c409dc0751c8bc3, 0x6f7e93c20796c7b8}},
+ {{0x1f5af604aea6ae05, 0xc12351f1bee49c99, 0x61a808b5eeff6b66, 0x0fcec10f01e02151}}},
+{{{0x644d58a649fe1e44, 0x21fcaea231ad777e, 0x02441c5a887fd0d2, 0x4901aa7183c511f3}},
+ {{0x3df2d29dc4244e45, 0x2b020e7493d8de0a, 0x6cc8067e820c214d, 0x413779166feab90a}},
+ {{0x08b1b7548c1af8f0, 0xce0f7a7c246299b4, 0xf760b0f91e06d939, 0x41bb887b726d1213}}},
+{{{0x9267806c567c49d8, 0x066d04ccca791e6a, 0xa69f5645e3cc394b, 0x5c95b686a0788cd2}},
+ {{0x97d980e0aa39f7d2, 0x35d0384252c6b51c, 0x7d43f49307cd55aa, 0x56bd36cfb78ac362}},
+ {{0x2ac519c10d14a954, 0xeaf474b494b5fa90, 0xe6af8382a9f87a5a, 0x0dea6db1879be094}}},
+{{{0xaa66bf547344e5ab, 0xda1258888f1b4309, 0x5e87d2b3fd564b2f, 0x5b2c78885483b1dd}},
+ {{0x15baeb74d6a8797a, 0x7ef55cf1fac41732, 0x29001f5a3c8b05c5, 0x0ad7cc8752eaccfb}},
+ {{0x52151362793408cf, 0xeb0f170319963d94, 0xa833b2fa883d9466, 0x093a7fa775003c78}}},
+{{{0xe5107de63a16d7be, 0xa377ffdc9af332cf, 0x70d5bf18440b677f, 0x6a252b19a4a31403}},
+ {{0xb8e9604460a91286, 0x7f3fd8047778d3de, 0x67d01e31bf8a5e2d, 0x7b038a06c27b653e}},
+ {{0x9ed919d5d36990f3, 0x5213aebbdb4eb9f2, 0xc708ea054cb99135, 0x58ded57f72260e56}}},
+{{{0x78e79dade9413d77, 0xf257f9d59729e67d, 0x59db910ee37aa7e6, 0x6aa11b5bbb9e039c}},
+ {{0xda6d53265b0fd48b, 0x8960823193bfa988, 0xd78ac93261d57e28, 0x79f2942d3a5c8143}},
+ {{0x97da2f25b6c88de9, 0x251ba7eaacf20169, 0x09b44f87ef4eb4e4, 0x7d90ab1bbc6a7da5}}},
+{{{0x9acca683a7016bfe, 0x90505f4df2c50b6d, 0x6b610d5fcce435aa, 0x19a10d446198ff96}},
+ {{0x1a07a3f496b3c397, 0x11ceaa188f4e2532, 0x7d9498d5a7751bf0, 0x19ed161f508dd8a0}},
+ {{0x560a2cd687dce6ca, 0x7f3568c48664cf4d, 0x8741e95222803a38, 0x483bdab1595653fc}}},
+{{{0xfa780f148734fa49, 0x106f0b70360534e0, 0x2210776fe3e307bd, 0x3286c109dde6a0fe}},
+ {{0xd6cf4d0ab4da80f6, 0x82483e45f8307fe0, 0x05005269ae6f9da4, 0x1c7052909cf7877a}},
+ {{0x32ee7de2874e98d4, 0x14c362e9b97e0c60, 0x5781dcde6a60a38a, 0x217dd5eaaa7aa840}}},
+{{{0x9db7c4d0248e1eb0, 0xe07697e14d74bf52, 0x1e6a9b173c562354, 0x7fa7c21f795a4965}},
+ {{0x8bdf1fb9be8c0ec8, 0x00bae7f8e30a0282, 0x4963991dad6c4f6c, 0x07058a6e5df6f60a}},
+ {{0xe9eb02c4db31f67f, 0xed25fd8910bcfb2b, 0x46c8131f5c5cddb4, 0x33b21c13a0cb9bce}}},
+{{{0x360692f8087d8e31, 0xf4dcc637d27163f7, 0x25a4e62065ea5963, 0x659bf72e5ac160d9}},
+ {{0x9aafb9b05ee38c5b, 0xbf9d2d4e071a13c7, 0x8eee6e6de933290a, 0x1c3bab17ae109717}},
+ {{0x1c9ab216c7cab7b0, 0x7d65d37407bbc3cc, 0x52744750504a58d5, 0x09f2606b131a2990}}},
+{{{0x40e87d44744346be, 0x1d48dad415b52b25, 0x7c3a8a18a13b603e, 0x4eb728c12fcdbdf7}},
+ {{0x7e234c597c6691ae, 0x64889d3d0a85b4c8, 0xdae2c90c354afae7, 0x0a871e070c6a9e1d}},
+ {{0x3301b5994bbc8989, 0x736bae3a5bdd4260, 0x0d61ade219d59e3c, 0x3ee7300f2685d464}}},
+{{{0xf5d255e49e7dd6b7, 0x8016115c610b1eac, 0x3c99975d92e187ca, 0x13815762979125c2}},
+ {{0x43fa7947841e7518, 0xe5c6fa59639c46d7, 0xa1065e1de3052b74, 0x7d47c6a2cfb89030}},
+ {{0x3fdad0148ef0d6e0, 0x9d3e749a91546f3c, 0x71ec621026bb8157, 0x148cf58d34c9ec80}}},
+{{{0x46a492f67934f027, 0x469984bef6840aa9, 0x5ca1bc2a89611854, 0x3ff2fa1ebd5dbbd4}},
+ {{0xe2572f7d9ae4756d, 0x56c345bb88f3487f, 0x9fd10b6d6960a88d, 0x278febad4eaea1b9}},
+ {{0xb1aa681f8c933966, 0x8c21949c20290c98, 0x39115291219d3c52, 0x4104dd02fe9c677b}}},
+{{{0x72b2bf5e1124422a, 0xa1fa0c3398a33ab5, 0x94cb6101fa52b666, 0x2c863b00afaf53d5}},
+ {{0x81214e06db096ab8, 0x21a8b6c90ce44f35, 0x6524c12a409e2af5, 0x0165b5a48efca481}},
+ {{0xf190a474a0846a76, 0x12eff984cd2f7cc0, 0x695e290658aa2b8f, 0x591b67d9bffec8b8}}},
+{{{0x312f0d1c80b49bfa, 0x5979515eabf3ec8a, 0x727033c09ef01c88, 0x3de02ec7ca8f7bcb}},
+ {{0x99b9b3719f18b55d, 0xe465e5faa18c641e, 0x61081136c29f05ed, 0x489b4f867030128b}},
+ {{0xd232102d3aeb92ef, 0xe16253b46116a861, 0x3d7eabe7190baa24, 0x49f5fbba496cbebf}}},
+{{{0x30949a108a5bcfd4, 0xdc40dd70bc6473eb, 0x92c294c1307c0d1c, 0x5604a86dcbfa6e74}},
+ {{0x155d628c1e9c572e, 0x8a4d86acc5884741, 0x91a352f6515763eb, 0x06a1a6c28867515b}},
+ {{0x7288d1d47c1764b6, 0x72541140e0418b51, 0x9f031a6018acf6d1, 0x20989e89fe2742c6}}},
+{{{0x499777fd3a2dcc7f, 0x32857c2ca54fd892, 0xa279d864d207e3a0, 0x0403ed1d0ca67e29}},
+ {{0x1674278b85eaec2e, 0x5621dc077acb2bdf, 0x640a4c1661cbf45a, 0x730b9950f70595d3}},
+ {{0xc94b2d35874ec552, 0xc5e6c8cf98246f8d, 0xf7cb46fa16c035ce, 0x5bd7454308303dcc}}},
+{{{0x7f9ad19528b24cc2, 0x7f6b54656335c181, 0x66b8b66e4fc07236, 0x133a78007380ad83}},
+ {{0x85c4932115e7792a, 0xc64c89a2bdcdddc9, 0x9d1e3da8ada3d762, 0x5bb7db123067f82c}},
+ {{0x0961f467c6ca62be, 0x04ec21d6211952ee, 0x182360779bd54770, 0x740dca6d58f0e0d2}}},
+{{{0x50b70bf5d3f0af0b, 0x4feaf48ae32e71f7, 0x60e84ed3a55bbd34, 0x00ed489b3f50d1ed}},
+ {{0x3906c72aed261ae5, 0x9ab68fd988e100f7, 0xf5e9059af3360197, 0x0e53dc78bf2b6d47}},
+ {{0xb90829bf7971877a, 0x5e4444636d17e631, 0x4d05c52e18276893, 0x27632d9a5a4a4af5}}},
+{{{0xd11ff05154b260ce, 0xd86dc38e72f95270, 0x601fcd0d267cc138, 0x2b67916429e90ccd}},
+ {{0xa98285d187eaffdb, 0xa5b4fbbbd8d0a864, 0xb658f27f022663f7, 0x3bbc2b22d99ce282}},
+ {{0xb917c952583c0a58, 0x653ff9b80fe4c6f3, 0x9b0da7d7bcdf3c0c, 0x43a0eeb6ab54d60e}}},
+{{{0x396966a46d4a5487, 0xf811a18aac2bb3ba, 0x66e4685b5628b26b, 0x70a477029d929b92}},
+ {{0x3ac6322357875fe8, 0xd9d4f4ecf5fbcb8f, 0x8dee8493382bb620, 0x50c5eaa14c799fdc}},
+ {{0xdd0edc8bd6f2fb3c, 0x54c63aa79cc7b7a0, 0xae0b032b2c8d9f1a, 0x6f9ce107602967fb}}},
+{{{0xad1054b1cde1c22a, 0xc4a8e90248eb32df, 0x5f3e7b33accdc0ea, 0x72364713fc79963e}},
+ {{0x139693063520e0b5, 0x437fcf7c88ea03fe, 0xf7d4c40bd3c959bc, 0x699154d1f893ded9}},
+ {{0x315d5c75b4b27526, 0xcccb842d0236daa5, 0x22f0c8a3345fee8e, 0x73975a617d39dbed}}},
+{{{0xe4024df96375da10, 0x78d3251a1830c870, 0x902b1948658cd91c, 0x7e18b10b29b7438a}},
+ {{0x6f37f392f4433e46, 0x0e19b9a11f566b18, 0x220fb78a1fd1d662, 0x362a4258a381c94d}},
+ {{0x9071d9132b6beb2f, 0x0f26e9ad28418247, 0xeab91ec9bdec925d, 0x4be65bc8f48af2de}}},
+{{{0x78487feba36e7028, 0x5f3f13001dd8ce34, 0x934fb12d4b30c489, 0x056c244d397f0a2b}},
+ {{0x1d50fba257c26234, 0x7bd4823adeb0678b, 0xc2b0dc6ea6538af5, 0x5665eec6351da73e}},
+ {{0xdb3ee00943bfb210, 0x4972018720800ac2, 0x26ab5d6173bd8667, 0x20b209c2ab204938}}},
+{{{0x549e342ac07fb34b, 0x02d8220821373d93, 0xbc262d70acd1f567, 0x7a92c9fdfbcac784}},
+ {{0x1fcca94516bd3289, 0x448d65aa41420428, 0x59c3b7b216a55d62, 0x49992cc64e612cd8}},
+ {{0x65bd1bea70f801de, 0x1befb7c0fe49e28a, 0xa86306cdb1b2ae4a, 0x3b7ac0cd265c2a09}}},
+{{{0x822bee438c01bcec, 0x530cb525c0fbc73b, 0x48519034c1953fe9, 0x265cc261e09a0f5b}},
+ {{0xf0d54e4f22ed39a7, 0xa2aae91e5608150a, 0xf421b2e9eddae875, 0x31bc531d6b7de992}},
+ {{0xdf3d134da980f971, 0x7a4fb8d1221a22a7, 0x3df7d42035aad6d8, 0x2a14edcc6a1a125e}}},
+{{{0xdf48ee0752cfce4e, 0xc3fffaf306ec08b7, 0x05710b2ab95459c4, 0x161d25fa963ea38d}},
+ {{0x231a8c570478433c, 0xb7b5270ec281439d, 0xdbaa99eae3d9079f, 0x2c03f5256c2b03d9}},
+ {{0x790f18757b53a47d, 0x307b0130cf0c5879, 0x31903d77257ef7f9, 0x699468bdbd96bbaf}}},
+{{{0xbd1f2f46f4dafecf, 0x7cef0114a47fd6f7, 0xd31ffdda4a47b37f, 0x525219a473905785}},
+ {{0xd8dd3de66aa91948, 0x485064c22fc0d2cc, 0x9b48246634fdea2f, 0x293e1c4e6c4a2e3a}},
+ {{0x376e134b925112e1, 0x703778b5dca15da0, 0xb04589af461c3111, 0x5b605c447f032823}}},
+{{{0xb965805920c47c89, 0xe7f0100c923b8fcc, 0x0001256502e2ef77, 0x24a76dcea8aeb3ee}},
+ {{0x3be9fec6f0e7f04c, 0x866a579e75e34962, 0x5542ef161e1de61a, 0x2f12fef4cc5abdd5}},
+ {{0x0a4522b2dfc0c740, 0x10d06e7f40c9a407, 0xc6cf144178cff668, 0x5e607b2518a43790}}},
+{{{0x58b31d8f6cdf1818, 0x35cfa74fc36258a2, 0xe1b3ff4f66e61d6e, 0x5067acab6ccdd5f7}},
+ {{0xa02c431ca596cf14, 0xe3c42d40aed3e400, 0xd24526802e0f26db, 0x201f33139e457068}},
+ {{0xfd527f6b08039d51, 0x18b14964017c0006, 0xd5220eb02e25a4a8, 0x397cba8862460375}}},
+{{{0x30c13093f05959b2, 0xe23aa18de9a97976, 0x222fd491721d5e26, 0x2339d320766e6c3a}},
+ {{0x7815c3fbc81379e7, 0xa6619420dde12af1, 0xffa9c0f885a8fdd5, 0x771b4022c1e1c252}},
+ {{0xd87dd986513a2fa7, 0xf5ac9b71f9d4cf08, 0xd06bc31b1ea283b3, 0x331a189219971a76}}},
+{{{0xf5166f45fb4f80c6, 0x9c36c7de61c775cf, 0xe3d4e81b9041d91c, 0x31167c6b83bdfe21}},
+ {{0x26512f3a9d7572af, 0x5bcbe28868074a9e, 0x84edc1c11180f7c4, 0x1ac9619ff649a67b}},
+ {{0xf22b3842524b1068, 0x5068343bee9ce987, 0xfc9d71844a6250c8, 0x612436341f08b111}}},
+{{{0xd99d41db874e898d, 0x09fea5f16c07dc20, 0x793d2c67d00f9bbc, 0x46ebe2309e5eff40}},
+ {{0x8b6349e31a2d2638, 0x9ddfb7009bd3fd35, 0x7f8bf1b8a3a06ba4, 0x1522aa3178d90445}},
+ {{0x2c382f5369614938, 0xdafe409ab72d6d10, 0xe8c83391b646f227, 0x45fe70f50524306c}}},
+{{{0xda4875a6960c0b8c, 0x5b68d076ef0e2f20, 0x07fb51cf3d0b8fd4, 0x428d1623a0e392d4}},
+ {{0x62f24920c8951491, 0x05f007c83f630ca2, 0x6fbb45d2f5c9d4b8, 0x16619f6db57a2245}},
+ {{0x084f4a4401a308fd, 0xa82219c376a5caac, 0xdeb8de4643d1bc7d, 0x1d81592d60bd38c6}}},
+{{{0xd833d7beec2a4c38, 0x2c9162830acc20ed, 0xe93a47aa92df7581, 0x702d67a3333c4a81}},
+ {{0x3a4a369a2f89c8a1, 0x63137a1d7c8de80d, 0xbcac008a78eda015, 0x2cb8b3a5b483b03f}},
+ {{0x36e417cbcb1b90a1, 0x33b3ddaa7f11794e, 0x3f510808885bc607, 0x24141dc0e6a8020d}}},
+{{{0x59f73c773fefee9d, 0xb3f1ef89c1cf989d, 0xe35dfb42e02e545f, 0x5766120b47a1b47c}},
+ {{0x91925dccbd83157d, 0x3ca1205322cc8094, 0x28e57f183f90d6e4, 0x1a4714cede2e767b}},
+ {{0xdb20ba0fb8b6b7ff, 0xb732c3b677511fa1, 0xa92b51c099f02d89, 0x4f3875ad489ca5f1}}},
+{{{0xc7fc762f4932ab22, 0x7ac0edf72f4c3c1b, 0x5f6b55aa9aa895e8, 0x3680274dad0a0081}},
+ {{0x79ed13f6ee73eec0, 0xa5c6526d69110bb1, 0xe48928c38603860c, 0x722a1446fd7059f5}},
+ {{0xd0959fe9a8cf8819, 0xd0a995508475a99c, 0x6eac173320b09cc5, 0x628ecf04331b1095}}},
+{{{0x98bcb118a9d0ddbc, 0xee449e3408b4802b, 0x87089226b8a6b104, 0x685f349a45c7915d}},
+ {{0x9b41acf85c74ccf1, 0xb673318108265251, 0x99c92aed11adb147, 0x7a47d70d34ecb40f}},
+ {{0x60a0c4cbcc43a4f5, 0x775c66ca3677bea9, 0xa17aa1752ff8f5ed, 0x11ded9020e01fdc0}}},
+{{{0x890e7809caefe704, 0x8728296de30e8c6c, 0x4c5cd2a392aeb1c9, 0x194263d15771531f}},
+ {{0x471f95b03bea93b7, 0x0552d7d43313abd3, 0xbd9370e2e17e3f7b, 0x7b120f1db20e5bec}},
+ {{0x17d2fb3d86502d7a, 0xb564d84450a69352, 0x7da962c8a60ed75d, 0x00d0f85b318736aa}}},
+{{{0x978b142e777c84fd, 0xf402644705a8c062, 0xa67ad51be7e612c7, 0x2f7b459698dd6a33}},
+ {{0xa6753c1efd7621c1, 0x69c0b4a7445671f5, 0x971f527405b23c11, 0x387bc74851a8c7cd}},
+ {{0x81894b4d4a52a9a8, 0xadd93e12f6b8832f, 0x184d8548b61bd638, 0x3f1c62dbd6c9f6cd}}},
+{{{0x2e8f1f0091910c1f, 0xa4df4fe0bff2e12c, 0x60c6560aee927438, 0x6338283facefc8fa}},
+ {{0x3fad3e40148f693d, 0x052656e194eb9a72, 0x2f4dcbfd184f4e2f, 0x406f8db1c482e18b}},
+ {{0x9e630d2c7f191ee4, 0x4fbf8301bc3ff670, 0x787d8e4e7afb73c4, 0x50d83d5be8f58fa5}}},
+{{{0x85683916c11a1897, 0x2d69a4efe506d008, 0x39af1378f664bd01, 0x65942131361517c6}},
+ {{0xc0accf90b4d3b66d, 0xa7059de561732e60, 0x033d1f7870c6b0ba, 0x584161cd26d946e4}},
+ {{0xbbf2b1a072d27ca2, 0xbf393c59fbdec704, 0xe98dbbcee262b81e, 0x02eebd0b3029b589}}},
+{{{0x61368756a60dac5f, 0x17e02f6aebabdc57, 0x7f193f2d4cce0f7d, 0x20234a7789ecdcf0}},
+ {{0x8765b69f7b85c5e8, 0x6ff0678bd168bab2, 0x3a70e77c1d330f9b, 0x3a5f6d51b0af8e7c}},
+ {{0x76d20db67178b252, 0x071c34f9d51ed160, 0xf62a4a20b3e41170, 0x7cd682353cffe366}}},
+{{{0x0be1a45bd887fab6, 0x2a846a32ba403b6e, 0xd9921012e96e6000, 0x2838c8863bdc0943}},
+ {{0xa665cd6068acf4f3, 0x42d92d183cd7e3d3, 0x5759389d336025d9, 0x3ef0253b2b2cd8ff}},
+ {{0xd16bb0cf4a465030, 0xfa496b4115c577ab, 0x82cfae8af4ab419d, 0x21dcb8a606a82812}}},
+{{{0x5c6004468c9d9fc8, 0x2540096ed42aa3cb, 0x125b4d4c12ee2f9c, 0x0bc3d08194a31dab}},
+ {{0x9a8d00fabe7731ba, 0x8203607e629e1889, 0xb2cc023743f3d97f, 0x5d840dbf6c6f678b}},
+ {{0x706e380d309fe18b, 0x6eb02da6b9e165c7, 0x57bbba997dae20ab, 0x3a4276232ac196dd}}},
+{{{0x4b42432c8a7084fa, 0x898a19e3dfb9e545, 0xbe9f00219c58e45d, 0x1ff177cea16debd1}},
+ {{0x3bf8c172db447ecb, 0x5fcfc41fc6282dbd, 0x80acffc075aa15fe, 0x0770c9e824e1a9f9}},
+ {{0xcf61d99a45b5b5fd, 0x860984e91b3a7924, 0xe7300919303e3e89, 0x39f264fd41500b1e}}},
+{{{0xa7ad3417dbe7e29c, 0xbd94376a2b9c139c, 0xa0e91b8e93597ba9, 0x1712d73468889840}},
+ {{0xd19b4aabfe097be1, 0xa46dfce1dfe01929, 0xc3c908942ca6f1ff, 0x65c621272c35f14e}},
+ {{0xe72b89f8ce3193dd, 0x4d103356a125c0bb, 0x0419a93d2e1cfe83, 0x22f9800ab19ce272}}},
+{{{0x605a368a3e9ef8cb, 0xe3e9c022a5504715, 0x553d48b05f24248f, 0x13f416cd647626e5}},
+ {{0x42029fdd9a6efdac, 0xb912cebe34a54941, 0x640f64b987bdf37b, 0x4171a4d38598cab4}},
+ {{0xfa2758aa99c94c8c, 0x23006f6fb000b807, 0xfbd291ddadda5392, 0x508214fa574bd1ab}}},
+{{{0xc20269153ed6fe4b, 0xa65a6739511d77c4, 0xcbde26462c14af94, 0x22f960ec6faba74b}},
+ {{0x461a15bb53d003d6, 0xb2102888bcf3c965, 0x27c576756c683a5a, 0x3a7758a4c86cb447}},
+ {{0x548111f693ae5076, 0x1dae21df1dfd54a6, 0x12248c90f3115e65, 0x5d9fd15f8de7f494}}},
+{{{0x031408d36d63727f, 0x6a379aefd7c7b533, 0xa9e18fc5ccaee24b, 0x332f35914f8fbed3}},
+ {{0x3f244d2aeed7521e, 0x8e3a9028432e9615, 0xe164ba772e9c16d4, 0x3bc187fa47eb98d8}},
+ {{0x6d470115ea86c20c, 0x998ab7cb6c46d125, 0xd77832b53a660188, 0x450d81ce906fba03}}},
+{{{0xf8ae4d2ad8453902, 0x7018058ee8db2d1d, 0xaab3995fc7d2c11e, 0x53b16d2324ccca79}},
+ {{0x23264d66b2cae0b5, 0x7dbaed33ebca6576, 0x030ebed6f0d24ac8, 0x2a887f78f7635510}},
+ {{0x2a23b9e75c012d4f, 0x0c974651cae1f2ea, 0x2fb63273675d70ca, 0x0ba7250b864403f5}}},
+{{{0xbb0d18fd029c6421, 0xbc2d142189298f02, 0x8347f8e68b250e96, 0x7b9f2fe8032d71c9}},
+ {{0xdd63589386f86d9c, 0x61699176e13a85a4, 0x2e5111954eaa7d57, 0x32c21b57fb60bdfb}},
+ {{0xd87823cd319e0780, 0xefc4cfc1897775c5, 0x4854fb129a0ab3f7, 0x12c49d417238c371}}},
+{{{0x0950b533ffe83769, 0x21861c1d8e1d6bd1, 0xf022d8381302e510, 0x2509200c6391cab4}},
+ {{0x09b3a01783799542, 0x626dd08faad5ee3f, 0xba00bceeeb70149f, 0x1421b246a0a444c9}},
+ {{0x4aa43a8e8c24a7c7, 0x04c1f540d8f05ef5, 0xadba5e0c0b3eb9dc, 0x2ab5504448a49ce3}}},
+{{{0x2ed227266f0f5dec, 0x9824ee415ed50824, 0x807bec7c9468d415, 0x7093bae1b521e23f}},
+ {{0xdc07ac631c5d3afa, 0x58615171f9df8c6c, 0x72a079d89d73e2b0, 0x7301f4ceb4eae15d}},
+ {{0x6409e759d6722c41, 0xa674e1cf72bf729b, 0xbc0a24eb3c21e569, 0x390167d24ebacb23}}},
+{{{0x27f58e3bba353f1c, 0x4c47764dbf6a4361, 0xafbbc4e56e562650, 0x07db2ee6aae1a45d}},
+ {{0xd7bb054ba2f2120b, 0xe2b9ceaeb10589b7, 0x3fe8bac8f3c0edbe, 0x4cbd40767112cb69}},
+ {{0x0b603cc029c58176, 0x5988e3825cb15d61, 0x2bb61413dcf0ad8d, 0x7b8eec6c74183287}}},
+{{{0xe4ca40782cd27cb0, 0xdaf9c323fbe967bd, 0xb29bd34a8ad41e9e, 0x72810497626ede4d}},
+ {{0x32fee570fc386b73, 0xda8b0141da3a8cc7, 0x975ffd0ac8968359, 0x6ee809a1b132a855}},
+ {{0x9444bb31fcfd863a, 0x2fe3690a3e4e48c5, 0xdc29c867d088fa25, 0x13bd1e38d173292e}}},
+{{{0xd32b4cd8696149b5, 0xe55937d781d8aab7, 0x0bcb2127ae122b94, 0x41e86fcfb14099b0}},
+ {{0x223fb5cf1dfac521, 0x325c25316f554450, 0x030b98d7659177ac, 0x1ed018b64f88a4bd}},
+ {{0x3630dfa1b802a6b0, 0x880f874742ad3bd5, 0x0af90d6ceec5a4d4, 0x746a247a37cdc5d9}}},
+{{{0xd531b8bd2b7b9af6, 0x5005093537fc5b51, 0x232fcf25c593546d, 0x20a365142bb40f49}},
+ {{0x6eccd85278d941ed, 0x2254ae83d22f7843, 0xc522d02e7bbfcdb7, 0x681e3351bff0e4e2}},
+ {{0x8b64b59d83034f45, 0x2f8b71f21fa20efb, 0x69249495ba6550e4, 0x539ef98e45d5472b}}},
+{{{0x6e7bb6a1a6205275, 0xaa4f21d7413c8e83, 0x6f56d155e88f5cb2, 0x2de25d4ba6345be1}},
+ {{0xd074d8961cae743f, 0xf86d18f5ee1c63ed, 0x97bdc55be7f4ed29, 0x4cbad279663ab108}},
+ {{0x80d19024a0d71fcd, 0xc525c20afb288af8, 0xb1a3974b5f3a6419, 0x7d7fbcefe2007233}}},
+{{{0xfaef1e6a266b2801, 0x866c68c4d5739f16, 0xf68a2fbc1b03762c, 0x5975435e87b75a8d}},
+ {{0xcd7c5dc5f3c29094, 0xc781a29a2a9105ab, 0x80c61d36421c3058, 0x4f9cd196dcd8d4d7}},
+ {{0x199297d86a7b3768, 0xd0d058241ad17a63, 0xba029cad5c1c0c17, 0x7ccdd084387a0307}}},
+{{{0xdca6422c6d260417, 0xae153d50948240bd, 0xa9c0c1b4fb68c677, 0x428bd0ed61d0cf53}},
+ {{0x9b0c84186760cc93, 0xcdae007a1ab32a99, 0xa88dec86620bda18, 0x3593ca848190ca44}},
+ {{0x9213189a5e849aa7, 0xd4d8c33565d8facd, 0x8c52545b53fdbbd1, 0x27398308da2d63e6}}},
+{{{0x42c38d28435ed413, 0xbd50f3603278ccc9, 0xbb07ab1a79da03ef, 0x269597aebe8c3355}},
+ {{0xb9a10e4c0a702453, 0x0fa25866d57d1bde, 0xffb9d9b5cd27daf7, 0x572c2945492c33fd}},
+ {{0xc77fc745d6cd30be, 0xe4dfe8d3e3baaefb, 0xa22c8830aa5dda0c, 0x7f985498c05bca80}}},
+{{{0x3849ce889f0be117, 0x8005ad1b7b54a288, 0x3da3c39f23fc921c, 0x76c2ec470a31f304}},
+ {{0xd35615520fbf6363, 0x08045a45cf4dfba6, 0xeec24fbc873fa0c2, 0x30f2653cd69b12e7}},
+ {{0x8a08c938aac10c85, 0x46179b60db276bcb, 0xa920c01e0e6fac70, 0x2f1273f1596473da}}},
+{{{0x4739fc7c8ae01e11, 0xfd5274904a6aab9f, 0x41d98a8287728f2e, 0x5d9e572ad85b69f2}},
+ {{0x30488bd755a70bc0, 0x06d6b5a4f1d442e7, 0xead1a69ebc596162, 0x38ac1997edc5f784}},
+ {{0x0666b517a751b13b, 0x747d06867e9b858c, 0xacacc011454dde49, 0x22dfcd9cbfe9e69c}}},
+{{{0x8ddbd2e0c30d0cd9, 0xad8e665facbb4333, 0x8f6b258c322a961f, 0x6b2916c05448c1c7}},
+ {{0x56ec59b4103be0a1, 0x2ee3baecd259f969, 0x797cb29413f5cd32, 0x0fe9877824cde472}},
+ {{0x7edb34d10aba913b, 0x4ea3cd822e6dac0e, 0x66083dff6578f815, 0x4c303f307ff00a17}}},
+{{{0xd30a3bd617b28c85, 0xc5d377b739773bea, 0xc6c6e78c1e6a5cbf, 0x0d61b8f78b2ab7c4}},
+ {{0x29fc03580dd94500, 0xecd27aa46fbbec93, 0x130a155fc2e2a7f8, 0x416b151ab706a1d5}},
+ {{0x56a8d7efe9c136b0, 0xbd07e5cd58e44b20, 0xafe62fda1b57e0ab, 0x191a2af74277e8d2}}},
+{{{0xd550095bab6f4985, 0x04f4cd5b4fbfaf1a, 0x9d8e2ed12a0c7540, 0x2bc24e04b2212286}},
+ {{0x09d4b60b2fe09a14, 0xc384f0afdbb1747e, 0x58e2ea8978b5fd6e, 0x519ef577b5e09b0a}},
+ {{0x1863d7d91124cca9, 0x7ac08145b88a708e, 0x2bcd7309857031f5, 0x62337a6e8ab8fae5}}},
+{{{0x4bcef17f06ffca16, 0xde06e1db692ae16a, 0x0753702d614f42b0, 0x5f6041b45b9212d0}},
+ {{0xd1ab324e1b3a1273, 0x18947cf181055340, 0x3b5d9567a98c196e, 0x7fa00425802e1e68}},
+ {{0x7d531574028c2705, 0x80317d69db0d75fe, 0x30fface8ef8c8ddd, 0x7e9de97bb6c3e998}}},
+{{{0x1558967b9e6585a3, 0x97c99ce098e98b92, 0x10af149b6eb3adad, 0x42181fe8f4d38cfa}},
+ {{0xf004be62a24d40dd, 0xba0659910452d41f, 0x81c45ee162a44234, 0x4cb829d8a22266ef}},
+ {{0x1dbcaa8407b86681, 0x081f001e8b26753b, 0x3cd7ce6a84048e81, 0x78af11633f25f22c}}},
+{{{0x8416ebd40b50babc, 0x1508722628208bee, 0xa3148fafb9c1c36d, 0x0d07daacd32d7d5d}},
+ {{0x3241c00e7d65318c, 0xe6bee5dcd0e86de7, 0x118b2dc2fbc08c26, 0x680d04a7fc603dc3}},
+ {{0xf9c2414a695aa3eb, 0xdaa42c4c05a68f21, 0x7c6c23987f93963e, 0x210e8cd30c3954e3}}},
+{{{0xac4201f210a71c06, 0x6a65e0aef3bfb021, 0xbc42c35c393632f7, 0x56ea8db1865f0742}},
+ {{0x2b50f16137fe6c26, 0xe102bcd856e404d8, 0x12b0f1414c561f6b, 0x51b17bc8d028ec91}},
+ {{0xfff5fb4bcf535119, 0xf4989d79df1108a0, 0xbdfcea659a3ba325, 0x18a11f1174d1a6f2}}},
+{{{0x407375ab3f6bba29, 0x9ec3b6d8991e482e, 0x99c80e82e55f92e9, 0x307c13b6fb0c0ae1}},
+ {{0xfbd63cdad27a5f2c, 0xf00fc4bc8aa106d7, 0x53fb5c1a8e64a430, 0x04eaabe50c1a2e85}},
+ {{0x24751021cb8ab5e7, 0xfc2344495c5010eb, 0x5f1e717b4e5610a1, 0x44da5f18c2710cd5}}},
+{{{0x033cc55ff1b82eb5, 0xb15ae36d411cae52, 0xba40b6198ffbacd3, 0x768edce1532e861f}},
+ {{0x9156fe6b89d8eacc, 0xe6b79451e23126a1, 0xbd7463d93944eb4e, 0x726373f6767203ae}},
+ {{0xe305ca72eb7ef68a, 0x662cf31f70eadb23, 0x18f026fdb4c45b68, 0x513b5384b5d2ecbd}}},
+{{{0x46d46280c729989e, 0x4b93fbd05368a5dd, 0x63df3f81d1765a89, 0x34cebd64b9a0a223}},
+ {{0x5e2702878af34ceb, 0x900b0409b946d6ae, 0x6512ebf7dabd8512, 0x61d9b76988258f81}},
+ {{0xa6c5a71349b7d94b, 0xa3f3d15823eb9446, 0x0416fbd277484834, 0x69d45e6f2c70812f}}},
+{{{0xce16f74bc53c1431, 0x2b9725ce2072edde, 0xb8b9c36fb5b23ee7, 0x7e2e0e450b5cc908}},
+ {{0x9fe62b434f460efb, 0xded303d4a63607d6, 0xf052210eb7a0da24, 0x237e7dbe00545b93}},
+ {{0x013575ed6701b430, 0x231094e69f0bfd10, 0x75320f1583e47f22, 0x71afa699b11155e3}}},
+{{{0x65ce6f9b3953b61d, 0xc65839eaafa141e6, 0x0f435ffda9f759fe, 0x021142e9c2b1c28e}},
+ {{0xea423c1c473b50d6, 0x51e87a1f3b38ef10, 0x9b84bf5fb2c9be95, 0x00731fbc78f89a1c}},
+ {{0xe430c71848f81880, 0xbf960c225ecec119, 0xb6dae0836bba15e3, 0x4c4d6f3347e15808}}},
+{{{0x18f7eccfc17d1fc9, 0x6c75f5a651403c14, 0xdbde712bf7ee0cdf, 0x193fddaaa7e47a22}},
+ {{0x2f0cddfc988f1970, 0x6b916227b0b9f51b, 0x6ec7b6c4779176be, 0x38bf9500a88f9fa8}},
+ {{0x1fd2c93c37e8876f, 0xa2f61e5a18d1462c, 0x5080f58239241276, 0x6a6fb99ebf0d4969}}},
+{{{0x6a46c1bb560855eb, 0x2416bb38f893f09d, 0xd71d11378f71acc1, 0x75f76914a31896ea}},
+ {{0xeeb122b5b6e423c6, 0x939d7010f286ff8e, 0x90a92a831dcf5d8c, 0x136fda9f42c5eb10}},
+ {{0xf94cdfb1a305bdd1, 0x0f364b9d9ff82c08, 0x2a87d8a5c3bb588a, 0x022183510be8dcba}}},
+{{{0x4af766385ead2d14, 0xa08ed880ca7c5830, 0x0d13a6e610211e3d, 0x6a071ce17b806c03}},
+ {{0x9d5a710143307a7f, 0xb063de9ec47da45f, 0x22bbfe52be927ad3, 0x1387c441fd40426c}},
+ {{0xb5d3c3d187978af8, 0x722b5a3d7f0e4413, 0x0d7b4848bb477ca0, 0x3171b26aaf1edc92}}},
+{{{0xa92f319097564ca8, 0xff7bb84c2275e119, 0x4f55fe37a4875150, 0x221fd4873cf0835a}},
+ {{0xa60db7d8b28a47d1, 0xa6bf14d61770a4f1, 0xd4a1f89353ddbd58, 0x6c514a63344243e9}},
+ {{0x2322204f3a156341, 0xfb73e0e9ba0a032d, 0xfce0dd4c410f030e, 0x48daa596fb924aaa}}},
+{{{0x6eca8e665ca59cc7, 0xa847254b2e38aca0, 0x31afc708d21e17ce, 0x676dd6fccad84af7}},
+ {{0x14f61d5dc84c9793, 0x9941f9e3ef418206, 0xcdf5b88f346277ac, 0x58c837fa0e8a79a9}},
+ {{0x0cf9688596fc9058, 0x1ddcbbf37b56a01b, 0xdcc2e77d4935d66a, 0x1c4f73f2c6a57f0a}}},
+{{{0x0e7a4fbd305fa0bb, 0x829d4ce054c663ad, 0xf421c3832fe33848, 0x795ac80d1bf64c42}},
+ {{0xb36e706efc7c3484, 0x73dfc9b4c3c1cf61, 0xeb1d79c9781cc7e5, 0x70459adb7daf675c}},
+ {{0x1b91db4991b42bb3, 0x572696234b02dcca, 0x9fdf9ee51f8c78dc, 0x5fe162848ce21fd3}}},
+{{{0xe2790aae4d077c41, 0x8b938270db7469a3, 0x6eb632dc8abd16a2, 0x720814ecaa064b72}},
+ {{0x315c29c795115389, 0xd7e0e507862f74ce, 0x0c4a762185927432, 0x72de6c984a25a1e4}},
+ {{0xae9ab553bf6aa310, 0x050a50a9806d6e1b, 0x92bb7403adff5139, 0x0394d27645be618b}}},
+{{{0x4d572251857eedf4, 0xe3724edde19e93c5, 0x8a71420e0b797035, 0x3b3c833687abe743}},
+ {{0xf5396425b23545a4, 0x15a7a27e98fbb296, 0xab6c52bc636fdd86, 0x79d995a8419334ee}},
+ {{0xcd8a8ea61195dd75, 0xa504d8a81dd9a82f, 0x540dca81a35879b6, 0x60dd16a379c86a8a}}},
+{{{0x35a2c8487381e559, 0x596ffea6d78082cb, 0xcb9771ebdba7b653, 0x5a08b5019b4da685}},
+ {{0x3501d6f8153e47b8, 0xb7a9675414a2f60c, 0x112ee8b6455d9523, 0x4e62a3c18112ea8a}},
+ {{0xc8d4ac04516ab786, 0x595af3215295b23d, 0xd6edd234db0230c1, 0x0929efe8825b41cc}}},
+{{{0x5f0601d1cbd0f2d3, 0x736e412f6132bb7f, 0x83604432238dde87, 0x1e3a5272f5c0753c}},
+ {{0x8b3172b7ad56651d, 0x01581b7a3fabd717, 0x2dc94df6424df6e4, 0x30376e5d2c29284f}},
+ {{0xd2918da78159a59c, 0x6bdc1cd93f0713f3, 0x565f7a934acd6590, 0x53daacec4cb4c128}}},
+{{{0x4ca73bd79cc8a7d6, 0x4d4a738f47e9a9b2, 0xf4cbf12942f5fe00, 0x01a13ff9bdbf0752}},
+ {{0x99852bc3852cfdb0, 0x2cc12e9559d6ed0b, 0x70f9e2bf9b5ac27b, 0x4f3b8c117959ae99}},
+ {{0x55b6c9c82ff26412, 0x1ac4a8c91fb667a8, 0xd527bfcfeb778bf2, 0x303337da7012a3be}}},
+{{{0x955422228c1c9d7c, 0x01fac1371a9b340f, 0x7e8d9177925b48d7, 0x53f8ad5661b3e31b}},
+ {{0x976d3ccbfad2fdd1, 0xcb88839737a640a8, 0x2ff00c1d6734cb25, 0x269ff4dc789c2d2b}},
+ {{0x0c003fbdc08d678d, 0x4d982fa37ead2b17, 0xc07e6bcdb2e582f1, 0x296c7291df412a44}}},
+{{{0x7903de2b33daf397, 0xd0ff0619c9a624b3, 0x8a1d252b555b3e18, 0x2b6d581c52e0b7c0}},
+ {{0xdfb23205dab8b59e, 0x465aeaa0c8092250, 0xd133c1189a725d18, 0x2327370261f117d1}},
+ {{0x3d0543d3623e7986, 0x679414c2c278a354, 0xae43f0cc726196f6, 0x7836c41f8245eaba}}},
+{{{0xe7a254db49e95a81, 0x5192d5d008b0ad73, 0x4d20e5b1d00afc07, 0x5d55f8012cf25f38}},
+ {{0xca651e848011937c, 0xc6b0c46e6ef41a28, 0xb7021ba75f3f8d52, 0x119dff99ead7b9fd}},
+ {{0x43eadfcbf4b31d4d, 0xc6503f7411148892, 0xfeee68c5060d3b17, 0x329293b3dd4a0ac8}}},
+{{{0x4e59214fe194961a, 0x49be7dc70d71cd4f, 0x9300cfd23b50f22d, 0x4789d446fc917232}},
+ {{0x2879852d5d7cb208, 0xb8dedd70687df2e7, 0xdc0bffab21687891, 0x2b44c043677daa35}},
+ {{0x1a1c87ab074eb78e, 0xfac6d18e99daf467, 0x3eacbbcd484f9067, 0x60c52eef2bb9a4e4}}},
+{{{0x0b5d89bc3bfd8bf1, 0xb06b9237c9f3551a, 0x0e4c16b0d53028f5, 0x10bc9c312ccfcaab}},
+ {{0x702bc5c27cae6d11, 0x44c7699b54a48cab, 0xefbc4056ba492eb2, 0x70d77248d9b6676d}},
+ {{0xaa8ae84b3ec2a05b, 0x98699ef4ed1781e0, 0x794513e4708e85d1, 0x63755bd3a976f413}}},
+{{{0xb55fa03e2ad10853, 0x356f75909ee63569, 0x9ff9f1fdbe69b890, 0x0d8cc1c48bc16f84}},
+ {{0x3dc7101897f1acb7, 0x5dda7d5ec165bbd8, 0x508e5b9c0fa1020f, 0x2763751737c52a56}},
+ {{0x029402d36eb419a9, 0xf0b44e7e77b460a5, 0xcfa86230d43c4956, 0x70c2dd8a7ad166e7}}},
+{{{0x656194509f6fec0e, 0xee2e7ea946c6518d, 0x9733c1f367e09b5c, 0x2e0fac6363948495}},
+ {{0x91d4967db8ed7e13, 0x74252f0ad776817a, 0xe40982e00d852564, 0x32b8613816a53ce5}},
+ {{0x79e7f7bee448cd64, 0x6ac83a67087886d0, 0xf89fd4d9a0e4db2e, 0x4179215c735a4f41}}},
+{{{0x8c7094e7d7dced2a, 0x97fb8ac347d39c70, 0xe13be033a906d902, 0x700344a30cd99d76}},
+ {{0xe4ae33b9286bcd34, 0xb7ef7eb6559dd6dc, 0x278b141fb3d38e1f, 0x31fa85662241c286}},
+ {{0xaf826c422e3622f4, 0xc12029879833502d, 0x9bc1b7e12b389123, 0x24bb2312a9952489}}},
+{{{0xb1a8ed1732de67c3, 0x3cb49418461b4948, 0x8ebd434376cfbcd2, 0x0fee3e871e188008}},
+ {{0x41f80c2af5f85c6b, 0x687284c304fa6794, 0x8945df99a3ba1bad, 0x0d1d2af9ffeb5d16}},
+ {{0xa9da8aa132621edf, 0x30b822a159226579, 0x4004197ba79ac193, 0x16acd79718531d76}}},
+{{{0x72df72af2d9b1d3d, 0x63462a36a432245a, 0x3ecea07916b39637, 0x123e0ef6b9302309}},
+ {{0xc959c6c57887b6ad, 0x94e19ead5f90feba, 0x16e24e62a342f504, 0x164ed34b18161700}},
+ {{0x487ed94c192fe69a, 0x61ae2cea3a911513, 0x877bf6d3b9a4de27, 0x78da0fc61073f3eb}}},
+{{{0x5bf15d28e52bc66a, 0x2c47e31870f01a8e, 0x2419afbc06c28bdd, 0x2d25deeb256b173a}},
+ {{0xa29f80f1680c3a94, 0x71f77e151ae9e7e6, 0x1100f15848017973, 0x054aa4b316b38ddd}},
+ {{0xdfc8468d19267cb8, 0x0b28789c66e54daf, 0x2aeb1d2a666eec17, 0x134610a6ab7da760}}},
+{{{0xcaf55ec27c59b23f, 0x99aeed3e154d04f2, 0x68441d72e14141f4, 0x140345133932a0a2}},
+ {{0xd91430e0dc028c3c, 0x0eb955a85217c771, 0x4b09e1ed2c99a1fa, 0x42881af2bd6a743c}},
+ {{0x7bfec69aab5cad3d, 0xc23e8cd34cb2cfad, 0x685dd14bfb37d6a2, 0x0ad6d64415677a18}}},
+{{{0x781a439e417becb5, 0x4ac5938cd10e0266, 0x5da385110692ac24, 0x11b065a2ade31233}},
+ {{0x7914892847927e9f, 0x33dad6ef370aa877, 0x1f8f24fa11122703, 0x5265ac2f2adf9592}},
+ {{0x405fdd309afcb346, 0xd9723d4428e63f54, 0x94c01df05f65aaae, 0x43e4dc3ae14c0809}}},
+{{{0xbc12c7f1a938a517, 0x473028ab3180b2e1, 0x3f78571efbcd254a, 0x74e534426ff6f90f}},
+ {{0xea6f7ac3adc2c6a3, 0xd0e928f6e9717c94, 0xe2d379ead645eaf5, 0x46dd8785c51ffbbe}},
+ {{0x709801be375c8898, 0x4b06dab5e3fd8348, 0x75880ced27230714, 0x2b09468fdd2f4c42}}},
+{{{0x97c749eeb701cb96, 0x83f438d4b6a369c3, 0x62962b8b9a402cd9, 0x6976c7509888df7b}},
+ {{0x5b97946582ffa02a, 0xda096a51fea8f549, 0xa06351375f77af9b, 0x1bcfde61201d1e76}},
+ {{0x4a4a5490246a59a2, 0xd63ebddee87fdd90, 0xd9437c670d2371fa, 0x69e87308d30f8ed6}}},
+{{{0x435a8bb15656beb0, 0xf8fac9ba4f4d5bca, 0xb9b278c41548c075, 0x3eb0ef76e892b622}},
+ {{0x0f80bf028bc80303, 0x6aae16b37a18cefb, 0xdd47ea47d72cd6a3, 0x61943588f4ed39aa}},
+ {{0xd26e5c3e91039f85, 0xc0e9e77df6f33aa9, 0xe8968c5570066a93, 0x3c34d1881faaaddd}}},
+{{{0x3f9d2b5ea09f9ec0, 0x1dab3b6fb623a890, 0xa09ba3ea72d926c4, 0x374193513fd8b36d}},
+ {{0xbd5b0b8f2fffe0d9, 0x6aa254103ed24fb9, 0x2ac7d7bcb26821c4, 0x605b394b60dca36a}},
+ {{0xb4e856e45a9d1ed2, 0xefe848766c97a9a2, 0xb104cf641e5eee7d, 0x2f50b81c88a71c8f}}},
+{{{0x31723c61fc6811bb, 0x9cb450486211800f, 0x768933d347995753, 0x3491a53502752fcd}},
+ {{0x2b552ca0a7da522a, 0x3230b336449b0250, 0xf2c4c5bca4b99fb9, 0x7b2c674958074a22}},
+ {{0xd55165883ed28cdf, 0x12d84fd2d362de39, 0x0a874ad3e3378e4f, 0x000d2b1f7c763e74}}},
+{{{0x3d420811d06d4a67, 0xbefc048590e0ffe3, 0xf870c6b7bd487bde, 0x6e2a7316319afa28}},
+ {{0x9624778c3e94a8ab, 0x0ad6f3cee9a78bec, 0x948ac7810d743c4f, 0x76627935aaecfccc}},
+ {{0x56a8ac24d6d59a9f, 0xc8db753e3096f006, 0x477f41e68f4c5299, 0x588d851cf6c86114}}},
+{{{0x51138ec78df6b0fe, 0x5397da89e575f51b, 0x09207a1d717af1b9, 0x2102fdba2b20d650}},
+ {{0xcd2a65e777d1f515, 0x548991878faa60f1, 0xb1b73bbcdabc06e5, 0x654878cba97cc9fb}},
+ {{0x969ee405055ce6a1, 0x36bca7681251ad29, 0x3a1af517aa7da415, 0x0ad725db29ecb2ba}}},
+{{{0xdc4267b1834e2457, 0xb67544b570ce1bc5, 0x1af07a0bf7d15ed7, 0x4aefcffb71a03650}},
+ {{0xfec7bc0c9b056f85, 0x537d5268e7f5ffd7, 0x77afc6624312aefa, 0x4f675f5302399fd9}},
+ {{0xc32d36360415171e, 0xcd2bef118998483b, 0x870a6eadd0945110, 0x0bccbb72a2a86561}}},
+{{{0x185e962feab1a9c8, 0x86e7e63565147dcd, 0xb092e031bb5b6df2, 0x4024f0ab59d6b73e}},
+ {{0x186d5e4c50fe1296, 0xe0397b82fee89f7e, 0x3bc7f6c5507031b0, 0x6678fd69108f37c2}},
+ {{0x1586fa31636863c2, 0x07f68c48572d33f2, 0x4f73cc9f789eaefc, 0x2d42e2108ead4701}}},
+{{{0x97f5131594dfd29b, 0x6155985d313f4c6a, 0xeba13f0708455010, 0x676b2608b8d2d322}},
+ {{0x21717b0d0f537593, 0x914e690b131e064c, 0x1bb687ae752ae09f, 0x420bf3a79b423c6e}},
+ {{0x8138ba651c5b2b47, 0x8671b6ec311b1b80, 0x7bff0cb1bc3135b0, 0x745d2ffa9c0cf1e0}}},
+{{{0xbf525a1e2bc9c8bd, 0xea5b260826479d81, 0xd511c70edf0155db, 0x1ae23ceb960cf5d0}},
+ {{0x6036df5721d34e6a, 0xb1db8827997bb3d0, 0xd3c209c3c8756afa, 0x06e15be54c1dc839}},
+ {{0x5b725d871932994a, 0x32351cb5ceb1dab0, 0x7dc41549dab7ca05, 0x58ded861278ec1f7}}},
+{{{0xd8173793f266c55c, 0xc8c976c5cc454e49, 0x5ce382f8bc26c3a8, 0x2ff39de85485f6f9}},
+ {{0x2dfb5ba8b6c2c9a8, 0x48eeef8ef52c598c, 0x33809107f12d1573, 0x08ba696b531d5bd8}},
+ {{0x77ed3eeec3efc57a, 0x04e05517d4ff4811, 0xea3d7a3ff1a671cb, 0x120633b4947cfe54}}},
+{{{0x0b94987891610042, 0x4ee7b13cecebfae8, 0x70be739594f0a4c0, 0x35d30a99b4d59185}},
+ {{0x82bd31474912100a, 0xde237b6d7e6fbe06, 0xe11e761911ea79c6, 0x07433be3cb393bde}},
+ {{0xff7944c05ce997f4, 0x575d3de4b05c51a3, 0x583381fd5a76847c, 0x2d873ede7af6da9f}}},
+{{{0x157a316443373409, 0xfab8b7eef4aa81d9, 0xb093fee6f5a64806, 0x2e773654707fa7b6}},
+ {{0xaa6202e14e5df981, 0xa20d59175015e1f5, 0x18a275d3bae21d6c, 0x0543618a01600253}},
+ {{0x0deabdf4974c23c1, 0xaa6f0a259dce4693, 0x04202cb8a29aba2c, 0x4b1443362d07960d}}},
+{{{0x47b837f753242cec, 0x256dc48cc04212f2, 0xe222fbfbe1d928c5, 0x48ea295bad8a2c07}},
+ {{0x299b1c3f57c5715e, 0x96cb929e6b686d90, 0x3004806447235ab3, 0x2c435c24a44d9fe1}},
+ {{0x0607c97c80f8833f, 0x0e851578ca25ec5b, 0x54f7450b161ebb6f, 0x7bcb4792a0def80e}}},
+{{{0x8487e3d02bc73659, 0x4baf8445059979df, 0xd17c975adcad6fbf, 0x57369f0bdefc96b6}},
+ {{0x1cecd0a0045224c2, 0x757f1b1b69e53952, 0x775b7a925289f681, 0x1b6cc62016736148}},
+ {{0xf1a9990175638698, 0x353dd1beeeaa60d3, 0x849471334c9ba488, 0x63fa6e6843ade311}}},
+{{{0xd15c20536597c168, 0x9f73740098d28789, 0x18aee7f13257ba1f, 0x3418bfda07346f14}},
+ {{0x2195becdd24b5eb7, 0x5e41f18cc0cd44f9, 0xdf28074441ca9ede, 0x07073b98f35b7d67}},
+ {{0xd03c676c4ce530d4, 0x0b64c0473b5df9f4, 0x065cef8b19b3a31e, 0x3084d661533102c9}}},
+{{{0xe1f6b79ebf8469ad, 0x15801004e2663135, 0x9a498330af74181b, 0x3ba2504f049b673c}},
+ {{0x9a6ce876760321fd, 0x7fe2b5109eb63ad8, 0x00e7d4ae8ac80592, 0x73d86b7abb6f723a}},
+ {{0x0b52b5606dba5ab6, 0xa9134f0fbbb1edab, 0x30a9520d9b04a635, 0x6813b8f37973e5db}}},
+{{{0x9854b054334127c1, 0x105d047882fbff25, 0xdb49f7f944186f4f, 0x1768e838bed0b900}},
+ {{0xf194ca56f3157e29, 0x136d35705ef528a5, 0xdd4cef778b0599bc, 0x7d5472af24f833ed}},
+ {{0xd0ef874daf33da47, 0x00d3be5db6e339f9, 0x3f2a8a2f9c9ceece, 0x5d1aeb792352435a}}},
+{{{0xf59e6bb319cd63ca, 0x670c159221d06839, 0xb06d565b2150cab6, 0x20fb199d104f12a3}},
+ {{0x12c7bfaeb61ba775, 0xb84e621fe263bffd, 0x0b47a5c35c840dcf, 0x7e83be0bccaf8634}},
+ {{0x61943dee6d99c120, 0x86101f2e460b9fe0, 0x6bb2f1518ee8598d, 0x76b76289fcc475cc}}},
+{{{0x791b4cc1756286fa, 0xdbced317d74a157c, 0x7e732421ea72bde6, 0x01fe18491131c8e9}},
+ {{0x4245f1a1522ec0b3, 0x558785b22a75656d, 0x1d485a2548a1b3c0, 0x60959eccd58fe09f}},
+ {{0x3ebfeb7ba8ed7a09, 0x49fdc2bbe502789c, 0x44ebce5d3c119428, 0x35e1eb55be947f4a}}},
+{{{0xdbdae701c5738dd3, 0xf9c6f635b26f1bee, 0x61e96a8042f15ef4, 0x3aa1d11faf60a4d8}},
+ {{0x14fd6dfa726ccc74, 0x3b084cfe2f53b965, 0xf33ae4f552a2c8b4, 0x59aab07a0d40166a}},
+ {{0x77bcec4c925eac25, 0x1848718460137738, 0x5b374337fea9f451, 0x1865e78ec8e6aa46}}},
+{{{0xccc4b7c7b66e1f7a, 0x44157e25f50c2f7e, 0x3ef06dfc713eaf1c, 0x582f446752da63f7}},
+ {{0x967c54e91c529ccb, 0x30f6269264c635fb, 0x2747aff478121965, 0x17038418eaf66f5c}},
+ {{0xc6317bd320324ce4, 0xa81042e8a4488bc4, 0xb21ef18b4e5a1364, 0x0c2a1c4bcda28dc9}}},
+{{{0xd24dc7d06f1f0447, 0xb2269e3edb87c059, 0xd15b0272fbb2d28f, 0x7c558bd1c6f64877}},
+ {{0xedc4814869bd6945, 0x0d6d907dbe1c8d22, 0xc63bd212d55cc5ab, 0x5a6a9b30a314dc83}},
+ {{0xd0ec1524d396463d, 0x12bb628ac35a24f0, 0xa50c3a791cbc5fa4, 0x0404a5ca0afbafc3}}},
+{{{0x8c1f40070aa743d6, 0xccbad0cb5b265ee8, 0x574b046b668fd2de, 0x46395bfdcadd9633}},
+ {{0x62bc9e1b2a416fd1, 0xb5c6f728e350598b, 0x04343fd83d5d6967, 0x39527516e7f8ee98}},
+ {{0x117fdb2d1a5d9a9c, 0x9c7745bcd1005c2a, 0xefd4bef154d56fea, 0x76579a29e822d016}}},
+{{{0x45b68e7e49c02a17, 0x23cd51a2bca9a37f, 0x3ed65f11ec224c1b, 0x43a384dc9e05bdb1}},
+ {{0x333cb51352b434f2, 0xd832284993de80e1, 0xb5512887750d35ce, 0x02c514bb2a2777c1}},
+ {{0x684bd5da8bf1b645, 0xfb8bd37ef6b54b53, 0x313916d7a9b0d253, 0x1160920961548059}}},
+{{{0xb44d166929dacfaa, 0xda529f4c8413598f, 0xe9ef63ca453d5559, 0x351e125bc5698e0b}},
+ {{0x7a385616369b4dcd, 0x75c02ca7655c3563, 0x7dc21bf9d4f18021, 0x2f637d7491e6e042}},
+ {{0xd4b49b461af67bbe, 0xd603037ac8ab8961, 0x71dee19ff9a699fb, 0x7f182d06e7ce2a9a}}},
+{{{0x7a7c8e64ab0168ec, 0xcb5a4a5515edc543, 0x095519d347cd0eda, 0x67d4ac8c343e93b0}},
+ {{0x09454b728e217522, 0xaa58e8f4d484b8d8, 0xd358254d7f46903c, 0x44acc043241c5217}},
+ {{0x1c7d6bbb4f7a5777, 0x8b35fed4918313e1, 0x4adca1c6c96b4684, 0x556d1c8312ad71bd}}},
+{{{0x17ef40e30c8d3982, 0x31f7073e15a3fa34, 0x4f21f3cb0773646e, 0x746c6c6d1d824eff}},
+ {{0x81f06756b11be821, 0x0faff82310a3f3dd, 0xf8b2d0556a99465d, 0x097abe38cc8c7f05}},
+ {{0x0c49c9877ea52da4, 0x4c4369559bdc1d43, 0x022c3809f7ccebd2, 0x577e14a34bee84bd}}},
+{{{0xf0e268ac61a73b0a, 0xf2fafa103791a5f5, 0xc1e13e826b6d00e9, 0x60fa7ee96fd78f42}},
+ {{0x94fecebebd4dd72b, 0xf46a4fda060f2211, 0x124a5977c0c8d1ff, 0x705304b8fb009295}},
+ {{0xb63d1d354d296ec6, 0xf3c3053e5fad31d8, 0x670b958cb4bd42ec, 0x21398e0ca16353fd}}},
+{{{0x216ab2ca8da7d2ef, 0x366ad9dd99f42827, 0xae64b9004fdd3c75, 0x403a395b53909e62}},
+ {{0x86c5fc16861b7e9a, 0xf6a330476a27c451, 0x01667267a1e93597, 0x05ffb9cd6082dfeb}},
+ {{0xa617fa9ff53f6139, 0x60f2b5e513e66cb6, 0xd7a8beefb3448aa4, 0x7a2932856f5ea192}}},
+{{{0x0b39d761b02de888, 0x5f550e7ed2414e1f, 0xa6bfa45822e1a940, 0x050a2f7dfd447b99}},
+ {{0xb89c444879639302, 0x4ae4f19350c67f2c, 0xf0b35da8c81af9c6, 0x39d0003546871017}},
+ {{0x437c3b33a650db77, 0x6bafe81dbac52bb2, 0xfe99402d2db7d318, 0x2b5b7eec372ba6ce}}},
+{{{0xb3bc4bbd83f50eef, 0x508f0c998c927866, 0x43e76587c8b7e66e, 0x0f7655a3a47f98d9}},
+ {{0xa694404d613ac8f4, 0x500c3c2bfa97e72c, 0x874104d21fcec210, 0x1b205fb38604a8ee}},
+ {{0x55ecad37d24b133c, 0x441e147d6038c90b, 0x656683a1d62c6fee, 0x0157d5dc87e0ecae}}},
+{{{0xf2a7af510354c13d, 0xd7a0b145aa372b60, 0x2869b96a05a3d470, 0x6528e42d82460173}},
+ {{0x95265514d71eb524, 0xe603d8815df14593, 0x147cdf410d4de6b7, 0x5293b1730437c850}},
+ {{0x23d0e0814bccf226, 0x92c745cd8196fb93, 0x8b61796c59541e5b, 0x40a44df0c021f978}}},
+{{{0xdaa869894f20ea6a, 0xea14a3d14c620618, 0x6001fccb090bf8be, 0x35f4e822947e9cf0}},
+ {{0x86c96e514bc5d095, 0xf20d4098fca6804a, 0x27363d89c826ea5d, 0x39ca36565719cacf}},
+ {{0x97506f2f6f87b75c, 0xc624aea0034ae070, 0x1ec856e3aad34dd6, 0x055b0be0e440e58f}}},
+{{{0x6469a17d89735d12, 0xdb6f27d5e662b9f1, 0x9fcba3286a395681, 0x363b8004d269af25}},
+ {{0x4d12a04b6ea33da2, 0x57cf4c15e36126dd, 0x90ec9675ee44d967, 0x64ca348d2a985aac}},
+ {{0x99588e19e4c4912d, 0xefcc3b4e1ca5ce6b, 0x4522ea60fa5b98d5, 0x7064bbab1de4a819}}},
+{{{0xb919e1515a770641, 0xa9a2e2c74e7f8039, 0x7527250b3df23109, 0x756a7330ac27b78b}},
+ {{0xa290c06142542129, 0xf2e2c2aebe8d5b90, 0xcf2458db76abfe1b, 0x02157ade83d626bf}},
+ {{0x3e46972a1b9a038b, 0x2e4ee66a7ee03fb4, 0x81a248776edbb4ca, 0x1a944ee88ecd0563}}},
+{{{0xd5a91d1151039372, 0x2ed377b799ca26de, 0xa17202acfd366b6b, 0x0730291bd6901995}},
+ {{0xbb40a859182362d6, 0xb99f55778a4d1abb, 0x8d18b427758559f6, 0x26c20fe74d26235a}},
+ {{0x648d1d9fe9cc22f5, 0x66bc561928dd577c, 0x47d3ed21652439d1, 0x49d271acedaf8b49}}},
+{{{0x89f5058a382b33f3, 0x5ae2ba0bad48c0b4, 0x8f93b503a53db36e, 0x5aa3ed9d95a232e6}},
+ {{0x2798aaf9b4b75601, 0x5eac72135c8dad72, 0xd2ceaa6161b7a023, 0x1bbfb284e98f7d4e}},
+ {{0x656777e9c7d96561, 0xcb2b125472c78036, 0x65053299d9506eee, 0x4a07e14e5e8957cc}}},
+{{{0x4ee412cb980df999, 0xa315d76f3c6ec771, 0xbba5edde925c77fd, 0x3f0bac391d313402}},
+ {{0x240b58cdc477a49b, 0xfd38dade6447f017, 0x19928d32a7c86aad, 0x50af7aed84afa081}},
+ {{0x6e4fde0115f65be5, 0x29982621216109b2, 0x780205810badd6d9, 0x1921a316baebd006}}},
+{{{0x89422f7edfb870fc, 0x2c296beb4f76b3bd, 0x0738f1d436c24df7, 0x6458df41e273aeb0}},
+ {{0xd75aad9ad9f3c18b, 0x566a0eef60b1c19c, 0x3e9a0bac255c0ed9, 0x7b049deca062c7f5}},
+ {{0xdccbe37a35444483, 0x758879330fedbe93, 0x786004c312c5dd87, 0x6093dccbc2950e64}}},
+{{{0x1ff39a8585e0706d, 0x36d0a5d8b3e73933, 0x43b9f2e1718f453b, 0x57d1ea084827a97c}},
+ {{0x6bdeeebe6084034b, 0x3199c2b6780fb854, 0x973376abb62d0695, 0x6e3180c98b647d90}},
+ {{0xee7ab6e7a128b071, 0xa4c1596d93a88baa, 0xf7b4de82b2216130, 0x363e999ddd97bd18}}},
+{{{0x96a843c135ee1fc4, 0x976eb35508e4c8cf, 0xb42f6801b58cd330, 0x48ee9b78693a052b}},
+ {{0x2f1848dce24baec6, 0x769b7255babcaf60, 0x90cb3c6e3cefe931, 0x231f979bc6f9b355}},
+ {{0x5c31de4bcc2af3c6, 0xb04bb030fe208d1f, 0xb78d7009c14fb466, 0x079bfa9b08792413}}},
+{{{0xe3903a51da300df4, 0x843964233da95ab0, 0xed3cf12d0b356480, 0x038c77f684817194}},
+ {{0xf3c9ed80a2d54245, 0x0aa08b7877f63952, 0xd76dac63d1085475, 0x1ef4fb159470636b}},
+ {{0x854e5ee65b167bec, 0x59590a4296d0cdc2, 0x72b2df3498102199, 0x575ee92a4a0bff56}}},
+{{{0xd4c080908a182fcf, 0x30e170c299489dbd, 0x05babd5752f733de, 0x43d4e7112cd3fd00}},
+ {{0x5d46bc450aa4d801, 0xc3af1227a533b9d8, 0x389e3b262b8906c2, 0x200a1e7e382f581b}},
+ {{0x518db967eaf93ac5, 0x71bc989b056652c0, 0xfe2b85d9567197f5, 0x050eca52651e4e38}}},
+{{{0xc3431ade453f0c9c, 0xe9f5045eff703b9b, 0xfcd97ac9ed847b3d, 0x4b0ee6c21c58f4c6}},
+ {{0x97ac397660e668ea, 0x9b19bbfe153ab497, 0x4cb179b534eca79f, 0x6151c09fa131ae57}},
+ {{0x3af55c0dfdf05d96, 0xdd262ee02ab4ee7a, 0x11b2bb8712171709, 0x1fef24fa800f030b}}},
+{{{0xb496123a6b6c6609, 0xa750fe8580ab5938, 0xf471bf39b7c27a5f, 0x507903ce77ac193c}},
+ {{0xff91a66a90166220, 0xf22552ae5bf1e009, 0x7dff85d87f90df7c, 0x4f620ffe0c736fb9}},
+ {{0x62f90d65dfde3e34, 0xcf28c592b9fa5fad, 0x99c86ef9c6164510, 0x25d448044a256c84}}},
+{{{0xbd68230ec7e9b16f, 0x0eb1b9c1c1c5795d, 0x7943c8c495b6b1ff, 0x2f9faf620bbacf5e}},
+ {{0x2c7c4415c9022b55, 0x56a0d241812eb1fe, 0xf02ea1c9d7b65e0d, 0x4180512fd5323b26}},
+ {{0xa4ff3e698a48a5db, 0xba6a3806bd95403b, 0x9f7ce1af47d5b65d, 0x15e087e55939d2fb}}},
+{{{0x12207543745c1496, 0xdaff3cfdda38610c, 0xe4e797272c71c34f, 0x39c07b1934bdede9}},
+ {{0x8894186efb963f38, 0x48a00e80dc639bd5, 0xa4e8092be96c1c99, 0x5a097d54ca573661}},
+ {{0x2d45892b17c9e755, 0xd033fd7289308df8, 0x6c2fe9d9525b8bd9, 0x2edbecf1c11cc079}}},
+{{{0x1616a4e3c715a0d2, 0x53623cb0f8341d4d, 0x96ef5329c7e899cb, 0x3d4e8dbba668baa6}},
+ {{0xee0f0fddd087a25f, 0x9c7531555c3e34ee, 0x660c572e8fab3ab5, 0x0854fc44544cd3b2}},
+ {{0x61eba0c555edad19, 0x24b533fef0a83de6, 0x3b77042883baa5f8, 0x678f82b898a47e8d}}},
+{{{0xb1491d0bd6900c54, 0x3539722c9d132636, 0x4db928920b362bc9, 0x4d7cd1fea68b69df}},
+ {{0x1e09d94057775696, 0xeed1265c3cd951db, 0xfa9dac2b20bce16f, 0x0f7f76e0e8d089f4}},
+ {{0x36d9ebc5d485b00c, 0xa2596492e4adb365, 0xc1659480c2119ccd, 0x45306349186e0d5f}}},
+{{{0x94ddd0c1a6cdff1d, 0x55f6f115e84213ae, 0x6c935f85992fcf6a, 0x067ee0f54a37f16f}},
+ {{0x96a414ec2b072491, 0x1bb2218127a7b65b, 0x6d2849596e8a4af0, 0x65f3b08ccd27765f}},
+ {{0xecb29fff199801f7, 0x9d361d1fa2a0f72f, 0x25f11d2375fd2f49, 0x124cefe80fe10fe2}}},
+{{{0x4c126cf9d18df255, 0xc1d471e9147a63b6, 0x2c6d3c73f3c93b5f, 0x6be3a6a2e3ff86a2}},
+ {{0x1518e85b31b16489, 0x8faadcb7db710bfb, 0x39b0bdf4a14ae239, 0x05f4cbea503d20c1}},
+ {{0xce040e9ec04145bc, 0xc71ff4e208f6834c, 0xbd546e8dab8847a3, 0x64666aa0a4d2aba5}}},
+{{{0x6841435a7c06d912, 0xca123c21bb3f830b, 0xd4b37b27b1cbe278, 0x1d753b84c76f5046}},
+ {{0xb0c53bf73337e94c, 0x7cb5697e11e14f15, 0x4b84abac1930c750, 0x28dd4abfe0640468}},
+ {{0x7dc0b64c44cb9f44, 0x18a3e1ace3925dbf, 0x7a3034862d0457c4, 0x4c498bf78a0c892e}}},
+{{{0x37d653fb1aa73196, 0x0f9495303fd76418, 0xad200b09fb3a17b2, 0x544d49292fc8613e}},
+ {{0x22d2aff530976b86, 0x8d90b806c2d24604, 0xdca1896c4de5bae5, 0x28005fe6c8340c17}},
+ {{0x6aefba9f34528688, 0x5c1bff9425107da1, 0xf75bbbcd66d94b36, 0x72e472930f316dfa}}},
+{{{0x2695208c9781084f, 0xb1502a0b23450ee1, 0xfd9daea603efde02, 0x5a9d2e8c2733a34c}},
+ {{0x07f3f635d32a7627, 0x7aaa4d865f6566f0, 0x3c85e79728d04450, 0x1fee7f000fe06438}},
+ {{0x765305da03dbf7e5, 0xa4daf2491434cdbd, 0x7b4ad5cdd24a88ec, 0x00f94051ee040543}}},
+{{{0x8d356b23c3d330b2, 0xf21c8b9bb0471b06, 0xb36c316c6e42b83c, 0x07d79c7e8beab10d}},
+ {{0xd7ef93bb07af9753, 0x583ed0cf3db766a7, 0xce6998bf6e0b1ec5, 0x47b7ffd25dd40452}},
+ {{0x87fbfb9cbc08dd12, 0x8a066b3ae1eec29b, 0x0d57242bdb1fc1bf, 0x1c3520a35ea64bb6}}},
+{{{0x80d253a6bccba34a, 0x3e61c3a13838219b, 0x90c3b6019882e396, 0x1c3d05775d0ee66f}},
+ {{0xcda86f40216bc059, 0x1fbb231d12bcd87e, 0xb4956a9e17c70990, 0x38750c3b66d12e55}},
+ {{0x692ef1409422e51a, 0xcbc0c73c2b5df671, 0x21014fe7744ce029, 0x0621e2c7d330487c}}},
+{{{0xaf9860cc8259838d, 0x90ea48c1c69f9adc, 0x6526483765581e30, 0x0007d6097bd3a5bc}},
+ {{0xb7ae1796b0dbf0f3, 0x54dfafb9e17ce196, 0x25923071e9aaa3b4, 0x5d8e589ca1002e9d}},
+ {{0xc0bf1d950842a94b, 0xb2d3c363588f2e3e, 0x0a961438bb51e2ef, 0x1583d7783c1cbf86}}},
+{{{0xeceea2ef5da27ae1, 0x597c3a1455670174, 0xc9a62a126609167a, 0x252a5f2e81ed8f70}},
+ {{0x90034704cc9d28c7, 0x1d1b679ef72cc58f, 0x16e12b5fbe5b8726, 0x4958064e83c5580a}},
+ {{0x0d2894265066e80d, 0xfcc3f785307c8c6b, 0x1b53da780c1112fd, 0x079c170bd843b388}}},
+{{{0x0506ece464fa6fff, 0xbee3431e6205e523, 0x3579422451b8ea42, 0x6dec05e34ac9fb00}},
+ {{0xcdd6cd50c0d5d056, 0x9af7686dbb03573b, 0x3ca6723ff3c3ef48, 0x6768c0d7317b8acc}},
+ {{0x94b625e5f155c1b3, 0x417bf3a7997b7b91, 0xc22cbddc6d6b2600, 0x51445e14ddcd52f4}}},
+{{{0x57502b4b3b144951, 0x8e67ff6b444bbcb3, 0xb8bd6927166385db, 0x13186f31e39295c8}},
+ {{0x893147ab2bbea455, 0x8c53a24f92079129, 0x4b49f948be30f7a7, 0x12e990086e4fd43d}},
+ {{0xf10c96b37fdfbb2e, 0x9f9a935e121ceaf9, 0xdf1136c43a5b983f, 0x77b2e3f05d3e99af}}},
+{{{0xfd0d75879cf12657, 0xe82fef94e53a0e29, 0xcc34a7f05bbb4be7, 0x0b251172a50c38a2}},
+ {{0x9532f48fcc5cd29b, 0x2ba851bea3ce3671, 0x32dacaa051122941, 0x478d99d9350004f2}},
+ {{0x1d5ad94890bb02c0, 0x50e208b10ec25115, 0xa26a22894ef21702, 0x4dc923343b524805}}},
+{{{0xe3828c400f8086b6, 0x3f77e6f7979f0dc8, 0x7ef6de304df42cb4, 0x5265797cb6abd784}},
+ {{0x3ad3e3ebf36c4975, 0xd75d25a537862125, 0xe873943da025a516, 0x6bbc7cb4c411c847}},
+ {{0x3c6f9cd1d4a50d56, 0xb6244077c6feab7e, 0x6ff9bf483580972e, 0x00375883b332acfb}}},
+{{{0x0001b2cd28cb0940, 0x63fb51a06f1c24c9, 0xb5ad8691dcd5ca31, 0x67238dbd8c450660}},
+ {{0xc98bec856c75c99c, 0xe44184c000e33cf4, 0x0a676b9bba907634, 0x669e2cb571f379d7}},
+ {{0xcb116b73a49bd308, 0x025aad6b2392729e, 0xb4793efa3f55d9b1, 0x72a1056140678bb9}}},
+{{{0xa2b6812b1cc9249d, 0x62866eee21211f58, 0x2cb5c5b85df10ece, 0x03a6b259e263ae00}},
+ {{0x0d8d2909e2e505b6, 0x98ca78abc0291230, 0x77ef5569a9b12327, 0x7c77897b81439b47}},
+ {{0xf1c1b5e2de331cb5, 0x5a9f5d8e15fca420, 0x9fa438f17bd932b1, 0x2a381bf01c6146e7}}},
+{{{0xac9b9879cfc811c1, 0x8b7d29813756e567, 0x50da4e607c70edfc, 0x5dbca62f884400b6}},
+ {{0xf7c0be32b534166f, 0x27e6ca6419cf70d4, 0x934df7d7a957a759, 0x5701461dabdec2aa}},
+ {{0x2c6747402c915c25, 0x1bdcd1a80b0d340a, 0x5e5601bd07b43f5f, 0x2555b4e05539a242}}},
+{{{0x6fc09f5266ddd216, 0xdce560a7c8e37048, 0xec65939da2df62fd, 0x7a869ae7e52ed192}},
+ {{0x78409b1d87e463d4, 0xad4da95acdfb639d, 0xec28773755259b9c, 0x69c806e9c31230ab}},
+ {{0x7b48f57414bb3f22, 0x68c7cee4aedccc88, 0xed2f936179ed80be, 0x25d70b885f77bc4b}}},
+{{{0x4151c3d9762bf4de, 0x083f435f2745d82b, 0x29775a2e0d23ddd5, 0x138e3a6269a5db24}},
+ {{0x98459d29bb1ae4d4, 0x56b9c4c739f954ec, 0x832743f6c29b4b3e, 0x21ea8e2798b6878a}},
+ {{0x87bef4b46a5a7b9c, 0xd2299d1b5fc1d062, 0x82409818dd321648, 0x5c5abeb1e5a2e03d}}},
+{{{0x14722af4b73c2ddb, 0xbc470c5f5a05060d, 0x00943eac2581b02e, 0x0e434b3b1f499c8f}},
+ {{0x02cde6de1306a233, 0x7b5a52a2116f8ec7, 0xe1c681f4c1163b5b, 0x241d350660d32643}},
+ {{0x6be4404d0ebc52c7, 0xae46233bb1a791f5, 0x2aec170ed25db42b, 0x1d8dfd966645d694}}},
+{{{0x296fa9c59c2ec4de, 0xbc8b61bf4f84f3cb, 0x1c7706d917a8f908, 0x63b795fc7ad3255d}},
+ {{0xd598639c12ddb0a4, 0xa5d19f30c024866b, 0xd17c2f0358fce460, 0x07a195152e095e8a}},
+ {{0xa8368f02389e5fc8, 0x90433b02cf8de43b, 0xafa1fd5dc5412643, 0x3e8fe83d032f0137}}},
+{{{0x2f8b15b90570a294, 0x94f2427067084549, 0xde1c5ae161bbfd84, 0x75ba3b797fac4007}},
+ {{0x08704c8de8efd13c, 0xdfc51a8e33e03731, 0xa59d5da51260cde3, 0x22d60899a6258c86}},
+ {{0x6239dbc070cdd196, 0x60fe8a8b6c7d8a9a, 0xb38847bceb401260, 0x0904d07b87779e5e}}},
+{{{0xb4ce1fd4ddba919c, 0xcf31db3ec74c8daa, 0x2c63cc63ad86cc51, 0x43e2143fbc1dde07}},
+ {{0xf4322d6648f940b9, 0x06952f0cbd2d0c39, 0x167697ada081f931, 0x6240aacebaf72a6c}},
+ {{0xf834749c5ba295a0, 0xd6947c5bca37d25a, 0x66f13ba7e7c9316a, 0x56bdaf238db40cac}}},
+{{{0x362ab9e3f53533eb, 0x338568d56eb93d40, 0x9e0e14521d5a5572, 0x1d24a86d83741318}},
+ {{0x1310d36cc19d3bb2, 0x062a6bb7622386b9, 0x7c9b8591d7a14f5c, 0x03aa31507e1e5754}},
+ {{0xf4ec7648ffd4ce1f, 0xe045eaf054ac8c1c, 0x88d225821d09357c, 0x43b261dc9aeb4859}}},
+{{{0xe55b1e1988bb79bb, 0xa09ed07dc17a359d, 0xb02c2ee2603dea33, 0x326055cf5b276bc2}},
+ {{0x19513d8b6c951364, 0x94fe7126000bf47b, 0x028d10ddd54f9567, 0x02b4d5e242940964}},
+ {{0xb4a155cb28d18df2, 0xeacc4646186ce508, 0xc49cf4936c824389, 0x27a6c809ae5d3410}}},
+{{{0x8ba6ebcd1f0db188, 0x37d3d73a675a5be8, 0xf22edfa315f5585a, 0x2cb67174ff60a17e}},
+ {{0xcd2c270ac43d6954, 0xdd4a3e576a66cab2, 0x79fa592469d7036c, 0x221503603d8c2599}},
+ {{0x59eecdf9390be1d0, 0xa9422044728ce3f1, 0x82891c667a94f0f4, 0x7b1df4b73890f436}}},
+{{{0xe492f2e0b3b2a224, 0x7c6c9e062b551160, 0x15eb8fe20d7f7b0e, 0x61fcef2658fc5992}},
+ {{0x5f2e221807f8f58c, 0xe3555c9fd49409d4, 0xb2aaa88d1fb6a630, 0x68698245d352e03d}},
+ {{0xdbb15d852a18187a, 0xf3e4aad386ddacd7, 0x44bae2810ff6c482, 0x46cf4c473daf01cf}}},
+{{{0x426525ed9ec4e5f9, 0x0e5eda0116903303, 0x72b1a7f2cbe5cadc, 0x29387bcd14eb5f40}},
+ {{0x213c6ea7f1498140, 0x7c1e7ef8392b4854, 0x2488c38c5629ceba, 0x1065aae50d8cc5bb}},
+ {{0x1c2c4525df200d57, 0x5c3b2dd6bfca674a, 0x0a07e7b1e1834030, 0x69a198e64f1ce716}}},
+{{{0x7afcd613efa9d697, 0x0cc45aa41c067959, 0xa56fe104c1fada96, 0x3a73b70472e40365}},
+ {{0x7b26e56b9e2d4734, 0xc4c7132b81c61675, 0xef5c9525ec9cde7f, 0x39c80b16e71743ad}},
+ {{0x0f196e0d1b826c68, 0xf71ff0e24960e3db, 0x6113167023b7436c, 0x0cf0ea5877da7282}}},
+{{{0x196c80a4ddd4ccbd, 0x22e6f55d95f2dd9d, 0xc75e33c740d6c71b, 0x7bb51279cb3c042f}},
+ {{0xe332ced43ba6945a, 0xde0b1361e881c05d, 0x1ad40f095e67ed3b, 0x5da8acdab8c63d5d}},
+ {{0xc4b6664a3a70159f, 0x76194f0f0a904e14, 0xa5614c39a4096c13, 0x6cd0ff50979feced}}},
+{{{0xc0e067e78f4428ac, 0x14835ab0a61135e3, 0xf21d14f338062935, 0x6390a4c8df04849c}},
+ {{0x7fecfabdb04ba18e, 0xd0fc7bfc3bddbcf7, 0xa41d486e057a131c, 0x641a4391f2223a61}},
+ {{0xc5c6b95aa606a8db, 0x914b7f9eb06825f1, 0x2a731f6b44fc9eff, 0x30ddf38562705cfc}}},
+{{{0x4e3dcbdad1bff7f9, 0xc9118e8220645717, 0xbacccebc0f189d56, 0x1b4822e9d4467668}},
+ {{0x33bef2bd68bcd52c, 0xc649dbb069482ef2, 0xb5b6ee0c41cb1aee, 0x5c294d270212a7e5}},
+ {{0xab360a7f25563781, 0x2512228a480f7958, 0xc75d05276114b4e3, 0x222d9625d976fe2a}}},
+{{{0x1c717f85b372ace1, 0x81930e694638bf18, 0x239cad056bc08b58, 0x0b34271c87f8fff4}},
+ {{0x0f94be7e0a344f85, 0xeb2faa8c87f22c38, 0x9ce1e75e4ee16f0f, 0x43e64e5418a08dea}},
+ {{0x8155e2521a35ce63, 0xbe100d4df912028e, 0xbff80bf8a57ddcec, 0x57342dc96d6bc6e4}}},
+{{{0xefeef065c8ce5998, 0xbf029510b5cbeaa2, 0x8c64a10620b7c458, 0x35134fb231c24855}},
+ {{0xf3c3bcb71e707bf6, 0x351d9b8c7291a762, 0x00502e6edad69a33, 0x522f521f1ec8807f}},
+ {{0x272c1f46f9a3902b, 0xc91ba3b799657bcc, 0xae614b304f8a1c0e, 0x7afcaad70b99017b}}},
+{{{0xc25ded54a4b8be41, 0x902d13e11bb0e2dd, 0x41f43233cde82ab2, 0x1085faa5c3aae7cb}},
+ {{0xa88141ecef842b6b, 0x55e7b14797abe6c5, 0x8c748f9703784ffe, 0x5b50a1f7afcd00b7}},
+ {{0x9b840f66f1361315, 0x18462242701003e9, 0x65ed45fae4a25080, 0x0a2862393fda7320}}},
+{{{0x46ab13c8347cbc9d, 0x3849e8d499c12383, 0x4cea314087d64ac9, 0x1f354134b1a29ee7}},
+ {{0x960e737b6ecb9d17, 0xfaf24948d67ceae1, 0x37e7a9b4d55e1b89, 0x5cb7173cb46c59eb}},
+ {{0x4a89e68b82b7abf0, 0xf41cd9279ba6b7b9, 0x16e6c210e18d876f, 0x7cacdb0f7f1b09c6}}},
+{{{0x9062b2e0d91a78bc, 0x47c9889cc8509667, 0x9df54a66405070b8, 0x7369e6a92493a1bf}},
+ {{0xe1014434dcc5caed, 0x47ed5d963c84fb33, 0x70019576ed86a0e7, 0x25b2697bd267f9e4}},
+ {{0x9d673ffb13986864, 0x3ca5fbd9415dc7b8, 0xe04ecc3bdf273b5e, 0x1420683db54e4cd2}}},
+{{{0xb478bd1e249dd197, 0x620c35005e58c102, 0xfb02d32fccbaac5c, 0x60b63bebf508a72d}},
+ {{0x34eebb6fc1cc5ad0, 0x6a1b0ce99646ac8b, 0xd3b0da49a66bde53, 0x31e83b4161d081c1}},
+ {{0x97e8c7129e062b4f, 0x49e48f4f29320ad8, 0x5bece14b6f18683f, 0x55cf1eb62d550317}}},
+{{{0x5879101065c23d58, 0x8b9d086d5094819c, 0xe2402fa912c55fa7, 0x669a6564570891d4}},
+ {{0x3076b5e37df58c52, 0xd73ab9dde799cc36, 0xbd831ce34913ee20, 0x1a56fbaa62ba0133}},
+ {{0x943e6b505c9dc9ec, 0x302557bba77c371a, 0x9873ae5641347651, 0x13c4836799c58a5c}}},
+{{{0x423a5d465ab3e1b9, 0xfc13c187c7f13f61, 0x19f83664ecb5b9b6, 0x66f80c93a637b607}},
+ {{0xc4dcfb6a5d8bd080, 0xdeebc4ec571a4842, 0xd4b2e883b8e55365, 0x50bdc87dc8e5b827}},
+ {{0x606d37836edfe111, 0x32353e15f011abd9, 0x64b03ac325b73b96, 0x1dd56444725fd5ae}}},
+{{{0x8fa47ff83362127d, 0xbc9f6ac471cd7c15, 0x6e71454349220c8b, 0x0e645912219f732e}},
+ {{0xc297e60008bac89a, 0x7d4cea11eae1c3e0, 0xf3e38be19fe7977c, 0x3a3a450f63a305cd}},
+ {{0x078f2f31d8394627, 0x389d3183de94a510, 0xd1e36c6d17996f80, 0x318c8d9393a9a87b}}},
+{{{0xf2745d032afffe19, 0x0c9f3c497f24db66, 0xbc98d3e3ba8598ef, 0x224c7c679a1d5314}},
+ {{0x5d669e29ab1dd398, 0xfc921658342d9e3b, 0x55851dfdf35973cd, 0x509a41c325950af6}},
+ {{0xbdc06edca6f925e9, 0x793ef3f4641b1f33, 0x82ec12809d833e89, 0x05bff02328a11389}}},
+{{{0x3632137023cae00b, 0x544acf0ad1accf59, 0x96741049d21a1c88, 0x780b8cc3fa2a44a7}},
+ {{0x6881a0dd0dc512e4, 0x4fe70dc844a5fafe, 0x1f748e6b8f4a5240, 0x576277cdee01a3ea}},
+ {{0x1ef38abc234f305f, 0x9a577fbd1405de08, 0x5e82a51434e62a0d, 0x5ff418726271b7a1}}},
+{{{0x398e080c1789db9d, 0xa7602025f3e778f5, 0xfa98894c06bd035d, 0x106a03dc25a966be}},
+ {{0xe5db47e813b69540, 0xf35d2a3b432610e1, 0xac1f26e938781276, 0x29d4db8ca0a0cb69}},
+ {{0xd9ad0aaf333353d0, 0x38669da5acd309e5, 0x3c57658ac888f7f0, 0x4ab38a51052cbefa}}},
+{{{0xdfdacbee4324c0e9, 0x054442883f955bb7, 0xdef7aaa8ea31609f, 0x68aee70642287cff}},
+ {{0xf68fe2e8809de054, 0xe3bc096a9c82bad1, 0x076353d40aadbf45, 0x7b9b1fb5dea1959e}},
+ {{0xf01cc8f17471cc0c, 0x95242e37579082bb, 0x27776093d3e46b5f, 0x2d13d55a28bd85fb}}},
+{{{0xfac5d2065b35b8da, 0xa8da8a9a85624bb7, 0xccd2ca913d21cd0f, 0x6b8341ee8bf90d58}},
+ {{0xbf019cce7aee7a52, 0xa8ded2b6e454ead3, 0x3c619f0b87a8bb19, 0x3619b5d7560916d8}},
+ {{0x3579f26b0282c4b2, 0x64d592f24fafefae, 0xb7cded7b28c8c7c0, 0x6a927b6b7173a8d7}}},
+{{{0x1f6db24f986e4656, 0x1021c02ed1e9105b, 0xf8ff3fff2cc0a375, 0x1d2a6bf8c6c82592}},
+ {{0x8d7040863ece88eb, 0xf0e307a980eec08c, 0xac2250610d788fda, 0x056d92a43a0d478d}},
+ {{0x1b05a196fc3da5a1, 0x77d7a8c243b59ed0, 0x06da3d6297d17918, 0x66fbb494f12353f7}}},
+{{{0x751a50b9d85c0fb8, 0xd1afdc258bcf097b, 0x2f16a6a38309a969, 0x14ddff9ee5b00659}},
+ {{0xd6d70996f12309d6, 0xdbfb2385e9c3d539, 0x46d602b0f7552411, 0x270a0b0557843e0c}},
+ {{0x61ff0640a7862bcc, 0x81cac09a5f11abfe, 0x9047830455d12abb, 0x19a4bde1945ae873}}},
+{{{0x9b9f26f520a6200a, 0x64804443cf13eaf8, 0x8a63673f8631edd3, 0x72bbbce11ed39dc1}},
+ {{0x40c709dec076c49f, 0x657bfaf27f3e53f6, 0x40662331eca042c4, 0x14b375487eb4df04}},
+ {{0xae853c94ab66dc47, 0xeb62343edf762d6e, 0xf08e0e186fb2f7d1, 0x4f0b1c02700ab37a}}},
+{{{0xe1706787d81951fa, 0xa10a2c8eb290c77b, 0xe7382fa03ed66773, 0x0a4d84710bcc4b54}},
+ {{0x79fd21ccc1b2e23f, 0x4ae7c281453df52a, 0xc8172ec9d151486b, 0x68abe9443e0a7534}},
+ {{0xda12c6c407831dcb, 0x0da230d74d5c510d, 0x4ab1531e6bd404e1, 0x4106b166bcf440ef}}},
+{{{0x02e57a421cd23668, 0x4ad9fb5d0eaef6fd, 0x954e6727b1244480, 0x7f792f9d2699f331}},
+ {{0xa485ccd539e4ecf2, 0x5aa3f3ad0555bab5, 0x145e3439937df82d, 0x1238b51e1214283f}},
+ {{0x0b886b925fd4d924, 0x60906f7a3626a80d, 0xecd367b4b98abd12, 0x2876beb1def344cf}}},
+{{{0xdc84e93563144691, 0x632fe8a0d61f23f4, 0x4caa800612a9a8d5, 0x48f9dbfa0e9918d3}},
+ {{0xd594b3333a8a85f8, 0x4ea37689e78d7d58, 0x73bf9f455e8e351f, 0x5507d7d2bc41ebb4}},
+ {{0x1ceb2903299572fc, 0x7c8ccaa29502d0ee, 0x91bfa43411cce67b, 0x5784481964a831e7}}},
+{{{0xda7c2b256768d593, 0x98c1c0574422ca13, 0xf1a80bd5ca0ace1d, 0x29cdd1adc088a690}},
+ {{0xd6cfd1ef5fddc09c, 0xe82b3efdf7575dce, 0x25d56b5d201634c2, 0x3041c6bb04ed2b9b}},
+ {{0x0ff2f2f9d956e148, 0xade797759f356b2e, 0x1a4698bb5f6c025c, 0x104bbd6814049a7b}}},
+{{{0x51f0fd3168f1ed67, 0x2c811dcdd86f3bc2, 0x44dc5c4304d2f2de, 0x5be8cc57092a7149}},
+ {{0xa95d9a5fd67ff163, 0xe92be69d4cc75681, 0xb7f8024cde20f257, 0x204f2a20fb072df5}},
+ {{0xc8143b3d30ebb079, 0x7589155abd652e30, 0x653c3c318f6d5c31, 0x2570fb17c279161f}}},
+{{{0x3efa367f2cb61575, 0xf5f96f761cd6026c, 0xe8c7142a65b52562, 0x3dcb65ea53030acd}},
+ {{0x192ea9550bb8245a, 0xc8e6fba88f9050d1, 0x7986ea2d88a4c935, 0x241c5f91de018668}},
+ {{0x28d8172940de6caa, 0x8fbf2cf022d9733a, 0x16d7fcdd235b01d1, 0x08420edd5fcdf0e5}}},
+{{{0xcdff20ab8362fa4a, 0x57e118d4e21a3e6e, 0xe3179617fc39e62b, 0x0d9a53efbc1769fd}},
+ {{0x0358c34e04f410ce, 0xb6135b5a276e0685, 0x5d9670c7ebb91521, 0x04d654f321db889c}},
+ {{0x5e7dc116ddbdb5d5, 0x2954deb68da5dd2d, 0x1cb608173334a292, 0x4a7a4f2618991ad7}}},
+{{{0xf4a718025fb15f95, 0x3df65f346b5c1b8f, 0xcdfcf08500e01112, 0x11b50c4cddd31848}},
+ {{0x24c3b291af372a4b, 0x93da8270718147f2, 0xdd84856486899ef2, 0x4a96314223e0ee33}},
+ {{0xa6e8274408a4ffd6, 0x738e177e9c1576d9, 0x773348b63d02b3f2, 0x4f4bce4dce6bcc51}}},
+{{{0xa71fce5ae2242584, 0x26ea725692f58a9e, 0xd21a09d71cea3cf4, 0x73fcdd14b71c01e6}},
+ {{0x30e2616ec49d0b6f, 0xe456718fcaec2317, 0x48eb409bf26b4fa6, 0x3042cee561595f37}},
+ {{0x427e7079449bac41, 0x855ae36dbce2310a, 0x4cae76215f841a7c, 0x389e740c9a9ce1d6}}},
+{{{0x64fcb3ae34dcb9ce, 0x97500323e348d0ad, 0x45b3f07d62c6381b, 0x61545379465a6788}},
+ {{0xc9bd78f6570eac28, 0xe55b0b3227919ce1, 0x65fc3eaba19b91ed, 0x25c425e5d6263690}},
+ {{0x3f3e06a6f1d7de6e, 0x3ef976278e062308, 0x8c14f6264e8a6c77, 0x6539a08915484759}}},
+{{{0xe9d21f74c3d2f773, 0xc150544125c46845, 0x624e5ce8f9b99e33, 0x11c5e4aac5cd186c}},
+ {{0xddc4dbd414bb4a19, 0x19b2bc3c98424f8e, 0x48a89fd736ca7169, 0x0f65320ef019bd90}},
+ {{0xd486d1b1cafde0c6, 0x4f3fe6e3163b5181, 0x59a8af0dfaf2939a, 0x4cabc7bdec33072a}}},
+{{{0x16faa8fb532f7428, 0xdbd42ea046a4e272, 0x5337653b8b9ea480, 0x4065947223973f03}},
+ {{0xf7c0a19c1a54a044, 0x4a1c5e2477bd9fbb, 0xa6e3ca115af22972, 0x1819bb953f2e9e0d}},
+ {{0x498fbb795e042e84, 0x7d0dd89a7698b714, 0x8bfb0ba427fe6295, 0x36ba82e721200524}}},
+{{{0xd60ecbb74245ec41, 0xfd9be89e34348716, 0xc9240afee42284de, 0x4472f648d0531db4}},
+ {{0xc8d69d0a57274ed5, 0x45ba803260804b17, 0xdf3cda102255dfac, 0x77d221232709b339}},
+ {{0x498a6d7064ad94d8, 0xa5b5c8fd9af62263, 0x8ca8ed0545c141f4, 0x2c63bec3662d358c}}},
+{{{0x7fe60d8bea787955, 0xb9dc117eb5f401b7, 0x91c7c09a19355cce, 0x22692ef59442bedf}},
+ {{0x9a518b3a8586f8bf, 0x9ee71af6cbb196f0, 0xaa0625e6a2385cf2, 0x1deb2176ddd7c8d1}},
+ {{0x8563d19a2066cf6c, 0x401bfd8c4dcc7cd7, 0xd976a6becd0d8f62, 0x67cfd773a278b05e}}},
+{{{0x8dec31faef3ee475, 0x99dbff8a9e22fd92, 0x512d11594e26cab1, 0x0cde561eec4310b9}},
+ {{0x2d5fa9855a4e586a, 0x65f8f7a449beab7e, 0xaa074dddf21d33d3, 0x185cba721bcb9dee}},
+ {{0x93869da3f4e3cb41, 0xbf0392f540f7977e, 0x026204fcd0463b83, 0x3ec91a769eec6eed}}},
+{{{0x1e9df75bf78166ad, 0x4dfda838eb0cd7af, 0xba002ed8c1eaf988, 0x13fedb3e11f33cfc}},
+ {{0x0fad2fb7b0a3402f, 0x46615ecbfb69f4a8, 0xf745bcc8c5f8eaa6, 0x7a5fa8794a94e896}},
+ {{0x52958faa13cd67a1, 0x965ee0818bdbb517, 0x16e58daa2e8845b3, 0x357d397d5499da8f}}},
+{{{0x1ebfa05fb0bace6c, 0xc934620c1caf9a1e, 0xcc771cc41d82b61a, 0x2d94a16aa5f74fec}},
+ {{0x481dacb4194bfbf8, 0x4d77e3f1bae58299, 0x1ef4612e7d1372a0, 0x3a8d867e70ff69e1}},
+ {{0x6f58cd5d55aff958, 0xba3eaa5c75567721, 0x75c123999165227d, 0x69be1343c2f2b35e}}},
+{{{0x0e091d5ee197c92a, 0x4f51019f2945119f, 0x143679b9f034e99c, 0x7d88112e4d24c696}},
+ {{0x82bbbdac684b8de3, 0xa2f4c7d03fca0718, 0x337f92fbe096aaa8, 0x200d4d8c63587376}},
+ {{0x208aed4b4893b32b, 0x3efbf23ebe59b964, 0xd762deb0dba5e507, 0x69607bd681bd9d94}}},
+{{{0xf6be021068de1ce1, 0xe8d518e70edcbc1f, 0xe3effdd01b5505a5, 0x35f63353d3ec3fd0}},
+ {{0x3b7f3bd49323a902, 0x7c21b5566b2c6e53, 0xe5ba8ff53a7852a7, 0x28bc77a5838ece00}},
+ {{0x63ba78a8e25d8036, 0x63651e0094333490, 0x48d82f20288ce532, 0x3a31abfa36b57524}}},
+{{{0x239e9624089c0a2e, 0xc748c4c03afe4738, 0x17dbed2a764fa12a, 0x639b93f0321c8582}},
+ {{0xc08f788f3f78d289, 0xfe30a72ca1404d9f, 0xf2778bfccf65cc9d, 0x7ee498165acb2021}},
+ {{0x7bd508e39111a1c3, 0x2b2b90d480907489, 0xe7d2aec2ae72fd19, 0x0edf493c85b602a6}}},
+{{{0xaecc8158599b5a68, 0xea574f0febade20e, 0x4fe41d7422b67f07, 0x403b92e3019d4fb4}},
+ {{0x6767c4d284764113, 0xa090403ff7f5f835, 0x1c8fcffacae6bede, 0x04c00c54d1dfa369}},
+ {{0x4dc22f818b465cf8, 0x71a0f35a1480eff8, 0xaee8bfad04c7d657, 0x355bb12ab26176f4}}},
+{{{0xa71e64cc7493bbf4, 0xe5bd84d9eca3b0c3, 0x0a6bc50cfa05e785, 0x0f9b8132182ec312}},
+ {{0xa301dac75a8c7318, 0xed90039db3ceaa11, 0x6f077cbf3bae3f2d, 0x7518eaf8e052ad8e}},
+ {{0xa48859c41b7f6c32, 0x0f2d60bcf4383298, 0x1815a929c9b1d1d9, 0x47c3871bbb1755c4}}},
+{{{0x5144539771ec4f48, 0xf805b17dc98c5d6e, 0xf762c11a47c3c66b, 0x00b89b85764699dc}},
+ {{0xfbe65d50c85066b0, 0x62ecc4b0b3a299b0, 0xe53754ea441ae8e0, 0x08fea02ce8d48d5f}},
+ {{0x824ddd7668deead0, 0xc86445204b685d23, 0xb514cfcd5d89d665, 0x473829a74f75d537}}},
+{{{0x82d2da754679c418, 0xe63bd7d8b2618df0, 0x355eef24ac47eb0a, 0x2078684c4833c6b4}},
+ {{0x23d9533aad3902c9, 0x64c2ddceef03588f, 0x15257390cfe12fb4, 0x6c668b4d44e4d390}},
+ {{0x3b48cf217a78820c, 0xf76a0ab281273e97, 0xa96c65a78c8eed7b, 0x7411a6054f8a433f}}},
+{{{0x4d659d32b99dc86d, 0x044cdc75603af115, 0xb34c712cdcc2e488, 0x7c136574fb8134ff}},
+ {{0x579ae53d18b175b4, 0x68713159f392a102, 0x8455ecba1eef35f5, 0x1ec9a872458c398f}},
+ {{0xb8e6a4d400a2509b, 0x9b81d7020bc882b4, 0x57e7cc9bf1957561, 0x3add88a5c7cd6460}}},
+{{{0xab895770b635dcf2, 0x02dfef6cf66c1fbc, 0x85530268beb6d187, 0x249929fccc879e74}},
+ {{0x85c298d459393046, 0x8f7e35985ff659ec, 0x1d2ca22af2f66e3a, 0x61ba1131a406a720}},
+ {{0xa3d0a0f116959029, 0x023b6b6cba7ebd89, 0x7bf15a3e26783307, 0x5620310cbbd8ece7}}},
+{{{0x528993434934d643, 0xb9dbf806a51222f5, 0x8f6d878fc3f41c22, 0x37676a2a4d9d9730}},
+ {{0x6646b5f477e285d6, 0x40e8ff676c8f6193, 0xa6ec7311abb594dd, 0x7ec846f3658cec4d}},
+ {{0x9b5e8f3f1da22ec7, 0x130f1d776c01cd13, 0x214c8fcfa2989fb8, 0x6daaf723399b9dd5}}},
+{{{0x591e4a5610628564, 0x2a4bb87ca8b4df34, 0xde2a2572e7a38e43, 0x3cbdabd9fee5046e}},
+ {{0x81aebbdd2cd13070, 0x962e4325f85a0e9e, 0xde9391aacadffecb, 0x53177fda52c230e6}},
+ {{0xa7bc970650b9de79, 0x3d12a7fbc301b59b, 0x02652e68d36ae38c, 0x79d739835a6199dc}}},
+{{{0xd9354df64131c1bd, 0x758094a186ec5822, 0x4464ee12e459f3c2, 0x6c11fce4cb133282}},
+ {{0x21c9d9920d591737, 0x9bea41d2e9b46cd6, 0xe20e84200d89bfca, 0x79d99f946eae5ff8}},
+ {{0xf17b483568673205, 0x387deae83caad96c, 0x61b471fd56ffe386, 0x31741195b745a599}}},
+{{{0xe8d10190b77a360b, 0x99b983209995e702, 0xbd4fdff8fa0247aa, 0x2772e344e0d36a87}},
+ {{0x17f8ba683b02a047, 0x50212096feefb6c8, 0x70139be21556cbe2, 0x203e44a11d98915b}},
+ {{0xd6863eba37b9e39f, 0x105bc169723b5a23, 0x104f6459a65c0762, 0x567951295b4d38d4}}},
+{{{0x535fd60613037524, 0xe210adf6b0fbc26a, 0xac8d0a9b23e990ae, 0x47204d08d72fdbf9}},
+ {{0x07242eb30d4b497f, 0x1ef96306b9bccc87, 0x37950934d8116f45, 0x05468d6201405b04}},
+ {{0x00f565a9f93267de, 0xcecfd78dc0d58e8a, 0xa215e2dcf318e28e, 0x4599ee919b633352}}},
+{{{0xd3c220ca70e0e76b, 0xb12bea58ea9f3094, 0x294ddec8c3271282, 0x0c3539e1a1d1d028}},
+ {{0xac746d6b861ae579, 0x31ab0650f6aea9dc, 0x241d661140256d4c, 0x2f485e853d21a5de}},
+ {{0x329744839c0833f3, 0x6fe6257fd2abc484, 0x5327d1814b358817, 0x65712585893fe9bc}}},
+{{{0x9c102fb732a61161, 0xe48e10dd34d520a8, 0x365c63546f9a9176, 0x32f6fe4c046f6006}},
+ {{0x81c29f1bd708ee3f, 0xddcb5a05ae6407d0, 0x97aec1d7d2a3eba7, 0x1590521a91d50831}},
+ {{0x40a3a11ec7910acc, 0x9013dff8f16d27ae, 0x1a9720d8abb195d4, 0x1bb9fe452ea98463}}},
+{{{0xe9d1d950b3d54f9e, 0x2d5f9cbee00d33c1, 0x51c2c656a04fc6ac, 0x65c091ee3c1cbcc9}},
+ {{0xcf5e6c95cc36747c, 0x294201536b0bc30d, 0x453ac67cee797af0, 0x5eae6ab32a8bb3c9}},
+ {{0x7083661114f118ea, 0x2b37b87b94349cad, 0x7273f51cb4e99f40, 0x78a2a95823d75698}}},
+{{{0xa2b072e95c8c2ace, 0x69cffc96651e9c4b, 0x44328ef842e7b42b, 0x5dd996c122aadeb3}},
+ {{0xb4f23c425ef83207, 0xabf894d3c9a934b5, 0xd0708c1339fd87f7, 0x1876789117166130}},
+ {{0x925b5ef0670c507c, 0x819bc842b93c33bf, 0x10792e9a70dd003f, 0x59ad4b7a6e28dc74}}},
+{{{0x5f3a7562eb3dbe47, 0xf7ea38548ebda0b8, 0x00c3e53145747299, 0x1304e9e71627d551}},
+ {{0x583b04bfacad8ea2, 0x29b743e8148be884, 0x2b1e583b0810c5db, 0x2b5449e58eb3bbaa}},
+ {{0x789814d26adc9cfe, 0x3c1bab3f8b48dd0b, 0xda0fe1fff979c60a, 0x4468de2d7c2dd693}}},
+{{{0x51bb355e9419469e, 0x33e6dc4c23ddc754, 0x93a5b6d6447f9962, 0x6cce7c6ffb44bd63}},
+ {{0x4b9ad8c6f86307ce, 0x21113531435d0c28, 0xd4a866c5657a772c, 0x5da6427e63247352}},
+ {{0x1a94c688deac22ca, 0xb9066ef7bbae1ff8, 0x88ad8c388d59580f, 0x58f29abfe79f2ca8}}},
+{{{0xe90ecfab8de73e68, 0x54036f9f377e76a5, 0xf0495b0bbe015982, 0x577629c4a7f41e36}},
+ {{0x4b5a64bf710ecdf6, 0xb14ce538462c293c, 0x3643d056d50b3ab9, 0x6af93724185b4870}},
+ {{0x3220024509c6a888, 0xd2e036134b558973, 0x83e236233c33289f, 0x701f25bb0caec18f}}},
+{{{0xc3a8b0f8e4616ced, 0xf700660e9e25a87d, 0x61e3061ff4bca59c, 0x2e0c92bfbdc40be9}},
+ {{0x9d18f6d97cbec113, 0x844a06e674bfdbe4, 0x20f5b522ac4e60d6, 0x720a5bc050955e51}},
+ {{0x0c3f09439b805a35, 0xe84e8b376242abfc, 0x691417f35c229346, 0x0e9b9cbb144ef0ec}}},
+{{{0xfbbad48ffb5720ad, 0xee81916bdbf90d0e, 0xd4813152635543bf, 0x221104eb3f337bd8}},
+ {{0x8dee9bd55db1beee, 0xc9c3ab370a723fb9, 0x44a8f1bf1c68d791, 0x366d44191cfd3cde}},
+ {{0x9e3c1743f2bc8c14, 0x2eda26fcb5856c3b, 0xccb82f0e68a7fb97, 0x4167a4e6bc593244}}},
+{{{0x643b9d2876f62700, 0x5d1d9d400e7668eb, 0x1b4b430321fc0684, 0x7938bb7e2255246a}},
+ {{0xc2be2665f8ce8fee, 0xe967ff14e880d62c, 0xf12e6e7e2f364eee, 0x34b33370cb7ed2f6}},
+ {{0xcdc591ee8681d6cc, 0xce02109ced85a753, 0xed7485c158808883, 0x1176fc6e2dfe65e4}}},
+{{{0xb4af6cd05b9c619b, 0x2ddfc9f4b2a58480, 0x3d4fa502ebe94dc4, 0x08fc3a4c677d5f34}},
+ {{0xdb90e28949770eb8, 0x98fbcc2aacf440a3, 0x21354ffeded7879b, 0x1f6a3e54f26906b6}},
+ {{0x60a4c199d30734ea, 0x40c085b631165cd6, 0xe2333e23f7598295, 0x4f2fad0116b900d1}}},
+{{{0x44beb24194ae4e54, 0x5f541c511857ef6c, 0xa61e6b2d368d0498, 0x445484a4972ef7ab}},
+ {{0x962cd91db73bb638, 0xe60577aafc129c08, 0x6f619b39f3b61689, 0x3451995f2944ee81}},
+ {{0x9152fcd09fea7d7c, 0x4a816c94b0935cf6, 0x258e9aaa47285c40, 0x10b89ca6042893b7}}},
+{{{0x9b2a426e3b646025, 0x32127190385ce4cf, 0xa25cffc2dd6dea45, 0x06409010bea8de75}},
+ {{0xd67cded679d34aa0, 0xcc0b9ec0cc4db39f, 0xa535a456e35d190f, 0x2e05d9eaf61f6fef}},
+ {{0xc447901ad61beb59, 0x661f19bce5dc880a, 0x24685482b7ca6827, 0x293c778cefe07f26}}},
+{{{0x86809e7007069096, 0xaad75b15e4e50189, 0x07f35715a21a0147, 0x0487f3f112815d5e}},
+ {{0x16c795d6a11ff200, 0xcb70d0e2b15815c9, 0x89f293209b5395b5, 0x50b8c2d031e47b4f}},
+ {{0x48350c08068a4962, 0x6ffdd05351092c9a, 0x17af4f4aaf6fc8dd, 0x4b0553b53cdba58b}}},
+{{{0x9c65fcbe1b32ff79, 0xeb75ea9f03b50f9b, 0xfced2a6c6c07e606, 0x35106cd551717908}},
+ {{0xbf05211b27c152d4, 0x5ec26849bd1af639, 0x5e0b2caa8e6fab98, 0x054c8bdd50bd0840}},
+ {{0x38a0b12f1dcf073d, 0x4b60a8a3b7f6a276, 0xfed5ac25d3404f9a, 0x72e82d5e5505c229}}},
+{{{0x6b0b697ff0d844c8, 0xbb12f85cd979cb49, 0xd2a541c6c1da0f1f, 0x7b7c242958ce7211}},
+ {{0x00d9cdfd69771d02, 0x410276cd6cfbf17e, 0x4c45306c1cb12ec7, 0x2857bf1627500861}},
+ {{0x9f21903f0101689e, 0xd779dfd3bf861005, 0xa122ee5f3deb0f1b, 0x510df84b485a00d4}}},
+{{{0xa54133bb9277a1fa, 0x74ec3b6263991237, 0x1a3c54dc35d2f15a, 0x2d347144e482ba3a}},
+ {{0x24b3c887c70ac15e, 0xb0f3a557fb81b732, 0x9b2cde2fe578cc1b, 0x4cf7ed0703b54f8e}},
+ {{0x6bd47c6598fbee0f, 0x9e4733e2ab55be2d, 0x1093f624127610c5, 0x4e05e26ad0a1eaa4}}},
+{{{0xda9b6b624b531f20, 0x429a760e77509abb, 0xdbe9f522e823cb80, 0x618f1856880c8f82}},
+ {{0x1833c773e18fe6c0, 0xe3c4711ad3c87265, 0x3bfd3c4f0116b283, 0x1955875eb4cd4db8}},
+ {{0x6da6de8f0e399799, 0x7ad61aa440fda178, 0xb32cd8105e3563dd, 0x15f6beae2ae340ae}}},
+{{{0x862bcb0c31ec3a62, 0x810e2b451138f3c2, 0x788ec4b839dac2a4, 0x28f76867ae2a9281}},
+ {{0xba9a0f7b9245e215, 0xf368612dd98c0dbb, 0x2e84e4cbf220b020, 0x6ba92fe962d90eda}},
+ {{0x3e4df9655884e2aa, 0xbd62fbdbdbd465a5, 0xd7596caa0de9e524, 0x6e8042ccb2b1b3d7}}},
+{{{0xf10d3c29ce28ca6e, 0xbad34540fcb6093d, 0xe7426ed7a2ea2d3f, 0x08af9d4e4ff298b9}},
+ {{0x1530653616521f7e, 0x660d06b896203dba, 0x2d3989bc545f0879, 0x4b5303af78ebd7b0}},
+ {{0x72f8a6c3bebcbde8, 0x4f0fca4adc3a8e89, 0x6fa9d4e8c7bfdf7a, 0x0dcf2d679b624eb7}}},
+{{{0x3d5947499718289c, 0x12ebf8c524533f26, 0x0262bfcb14c3ef15, 0x20b878d577b7518e}},
+ {{0x753941be5a45f06e, 0xd07caeed6d9c5f65, 0x11776b9c72ff51b6, 0x17d2d1d9ef0d4da9}},
+ {{0x27f2af18073f3e6a, 0xfd3fe519d7521069, 0x22e3b72c3ca60022, 0x72214f63cc65c6a7}}},
+{{{0xb4e37f405307a693, 0xaba714d72f336795, 0xd6fbd0a773761099, 0x5fdf48c58171cbc9}},
+ {{0x1d9db7b9f43b29c9, 0xd605824a4f518f75, 0xf2c072bd312f9dc4, 0x1f24ac855a1545b0}},
+ {{0x24d608328e9505aa, 0x4748c1d10c1420ee, 0xc7ffe45c06fb25a2, 0x00ba739e2ae395e6}}},
+{{{0x592e98de5c8790d6, 0xe5bfb7d345c2a2df, 0x115a3b60f9b49922, 0x03283a3e67ad78f3}},
+ {{0xae4426f5ea88bb26, 0x360679d984973bfb, 0x5c9f030c26694e50, 0x72297de7d518d226}},
+ {{0x48241dc7be0cb939, 0x32f19b4d8b633080, 0xd3dfc90d02289308, 0x05e1296846271945}}},
+{{{0xba82eeb32d9c495a, 0xceefc8fcf12bb97c, 0xb02dabae93b5d1e0, 0x39c00c9c13698d9b}},
+ {{0xadbfbbc8242c4550, 0xbcc80cecd03081d9, 0x843566a6f5c8df92, 0x78cf25d38258ce4c}},
+ {{0x15ae6b8e31489d68, 0xaa851cab9c2bf087, 0xc9a75a97f04efa05, 0x006b52076b3ff832}}},
+{{{0x29e0cfe19d95781c, 0xb681df18966310e2, 0x57df39d370516b39, 0x4d57e3443bc76122}},
+ {{0xf5cb7e16b9ce082d, 0x3407f14c417abc29, 0xd4b36bce2bf4a7ab, 0x7de2e9561a9f75ce}},
+ {{0xde70d4f4b6a55ecb, 0x4801527f5d85db99, 0xdbc9c440d3ee9a81, 0x6b2a90af1a6029ed}}},
+{{{0x6923f4fc9ae61e97, 0x5735281de03f5fd1, 0xa764ae43e6edd12d, 0x5fd8f4e9d12d3e4a}},
+ {{0x77ebf3245bb2d80a, 0xd8301b472fb9079b, 0xc647e6f24cee7333, 0x465812c8276c2109}},
+ {{0x4d43beb22a1062d9, 0x7065fb753831dc16, 0x180d4a7bde2968d7, 0x05b32c2b1cb16790}}},
+{{{0xc8c05eccd24da8fd, 0xa1cf1aac05dfef83, 0xdbbeeff27df9cd61, 0x3b5556a37b471e99}},
+ {{0xf7fca42c7ad58195, 0x3214286e4333f3cc, 0xb6c29d0d340b979d, 0x31771a48567307e1}},
+ {{0x32b0c524e14dd482, 0xedb351541a2ba4b6, 0xa3d16048282b5af3, 0x4fc079d27a7336eb}}},
+{{{0x51c938b089bf2f7f, 0x2497bd6502dfe9a7, 0xffffc09c7880e453, 0x124567cecaf98e92}},
+ {{0xdc348b440c86c50d, 0x1337cbc9cc94e651, 0x6422f74d643e3cb9, 0x241170c2bae3cd08}},
+ {{0x3ff9ab860ac473b4, 0xf0911dee0113e435, 0x4ae75060ebc6c4af, 0x3f8612966c87000d}}},
+{{{0x0c9c5303f7957be4, 0xa3c31a20e085c145, 0xb0721d71d0850050, 0x0aba390eab0bf2da}},
+ {{0x529fdffe638c7bf3, 0xdf2b9e60388b4995, 0xe027b34f1bad0249, 0x7bc92fc9b9fa74ed}},
+ {{0x9f97ef2e801ad9f9, 0x83697d5479afda3a, 0xe906b3ffbd596b50, 0x02672b37dd3fb8e0}}},
+{{{0x48b2ca8b260885e4, 0xa4286bec82b34c1c, 0x937e1a2617f58f74, 0x741d1fcbab2ca2a5}},
+ {{0xee9ba729398ca7f5, 0xeb9ca6257a4849db, 0x29eb29ce7ec544e1, 0x232ca21ef736e2c8}},
+ {{0xbf61423d253fcb17, 0x08803ceafa39eb14, 0xf18602df9851c7af, 0x0400f3a049e3414b}}},
+{{{0xabce0476ba61c55b, 0x36a3d6d7c4d39716, 0x6eb259d5e8d82d09, 0x0c9176e984d756fb}},
+ {{0x2efba412a06e7b06, 0x146785452c8d2560, 0xdf9713ebd67a91c7, 0x32830ac7157eadf3}},
+ {{0x0e782a7ab73769e8, 0x04a05d7875b18e2c, 0x29525226ebcceae1, 0x0d794f8383eba820}}},
+{{{0xff35f5cb9e1516f4, 0xee805bcf648aae45, 0xf0d73c2bb93a9ef3, 0x097b0bf22092a6c2}},
+ {{0x7be44ce7a7a2e1ac, 0x411fd93efad1b8b7, 0x1734a1d70d5f7c9b, 0x0d6592233127db16}},
+ {{0xc48bab1521a9d733, 0xa6c2eaead61abb25, 0x625c6c1cc6cb4305, 0x7fc90fea93eb3a67}}},
+{{{0x0408f1fe1f5c5926, 0x1a8f2f5e3b258bf4, 0x40a951a2fdc71669, 0x6598ee93c98b577e}},
+ {{0xc527deb59c7cb23d, 0x955391695328404e, 0xd64392817ccf2c7a, 0x6ce97dabf7d8fa11}},
+ {{0x25b5a8e50ef7c48f, 0xeb6034116f2ce532, 0xc5e75173e53de537, 0x73119fa08c12bb03}}},
+{{{0xed30129453f1a4cb, 0xbce621c9c8f53787, 0xfacb2b1338bee7b9, 0x3025798a9ea8428c}},
+ {{0x7845b94d21f4774d, 0xbf62f16c7897b727, 0x671857c03c56522b, 0x3cd6a85295621212}},
+ {{0x3fecde923aeca999, 0xbdaa5b0062e8c12f, 0x67b99dfc96988ade, 0x3f52c02852661036}}},
+{{{0xffeaa48e2a1351c6, 0x28624754fa7f53d7, 0x0b5ba9e57582ddf1, 0x60c0104ba696ac59}},
+ {{0x9258bf99eec416c6, 0xac8a5017a9d2f671, 0x629549ab16dea4ab, 0x05d0e85c99091569}},
+ {{0x051de020de9cbe97, 0xfa07fc56b50bcf74, 0x378cec9f0f11df65, 0x36853c69ab96de4d}}},
+{{{0x36d9b8de78f39b2d, 0x7f42ed71a847b9ec, 0x241cd1d679bd3fde, 0x6a704fec92fbce6b}},
+ {{0x4433c0b0fac5e7be, 0x724bae854c08dcbe, 0xf1f24cc446978f9b, 0x4a0aff6d62825fc8}},
+ {{0xe917fb9e61095301, 0xc102df9402a092f8, 0xbf09e2f5fa66190b, 0x681109bee0dcfe37}}},
+{{{0x559a0cc9782a0dde, 0x551dcdb2ea718385, 0x7f62865b31ef238c, 0x504aa7767973613d}},
+ {{0x9c18fcfa36048d13, 0x29159db373899ddd, 0xdc9f350b9f92d0aa, 0x26f57eee878a19d4}},
+ {{0x0cab2cd55687efb1, 0x5180d162247af17b, 0x85c15a344f5a2467, 0x4041943d9dba3069}}},
+{{{0xc3c0eeba43ebcc96, 0x8d749c9c26ea9caf, 0xd9fa95ee1c77ccc6, 0x1420a1d97684340f}},
+ {{0x4b217743a26caadd, 0x47a6b424648ab7ce, 0xcb1d4f7a03fbc9e3, 0x12d931429800d019}},
+ {{0x00c67799d337594f, 0x5e3c5140b23aa47b, 0x44182854e35ff395, 0x1b4f92314359a012}}},
+{{{0x3e5c109d89150951, 0x39cefa912de9696a, 0x20eae43f975f3020, 0x239b572a7f132dae}},
+ {{0x33cf3030a49866b1, 0x251f73d2215f4859, 0xab82aa4051def4f6, 0x5ff191d56f9a23f6}},
+ {{0x819ed433ac2d9068, 0x2883ab795fc98523, 0xef4572805593eb3d, 0x020c526a758f36cb}}},
+{{{0x779834f89ed8dbbc, 0xc8f2aaf9dc7ca46c, 0xa9524cdca3e1b074, 0x02aacc4615313877}},
+ {{0xe931ef59f042cc89, 0x2c589c9d8e124bb6, 0xadc8e18aaec75997, 0x452cfe0a5602c50c}},
+ {{0x86a0f7a0647877df, 0xbbc464270e607c9f, 0xab17ea25f1fb11c9, 0x4cfb7d7b304b877b}}},
+{{{0x72b43d6cb89b75fe, 0x54c694d99c6adc80, 0xb8c3aa373ee34c9f, 0x14b4622b39075364}},
+ {{0xe28699c29789ef12, 0x2b6ecd71df57190d, 0xc343c857ecc970d0, 0x5b1d4cbc434d3ac5}},
+ {{0xb6fb2615cc0a9f26, 0x3a4f0e2bb88dcce5, 0x1301498b3369a705, 0x2f98f71258592dd1}}},
+{{{0x0c94a74cb50f9e56, 0x5b1ff4a98e8e1320, 0x9a2acc2182300f67, 0x3a6ae249d806aaf9}},
+ {{0x2e12ae444f54a701, 0xfcfe3ef0a9cbd7de, 0xcebf890d75835de0, 0x1d8062e9e7614554}},
+ {{0x657ada85a9907c5a, 0x1a0ea8b591b90f62, 0x8d0e1dfbdf34b4e9, 0x298b8ce8aef25ff3}}},
+{{{0x2a927953eff70cb2, 0x4b89c92a79157076, 0x9418457a30a7cf6a, 0x34b8a8404d5ce485}},
+ {{0x837a72ea0a2165de, 0x3fab07b40bcf79f6, 0x521636c77738ae70, 0x6ba6271803a7d7dc}},
+ {{0xc26eecb583693335, 0xd5a813df63b5fefd, 0xa293aa9aa4b22573, 0x71d62bdd465e1c6a}}},
+{{{0x6533cc28d378df80, 0xf6db43790a0fa4b4, 0xe3645ff9f701da5a, 0x74d5f317f3172ba4}},
+ {{0xcd2db5dab1f75ef5, 0xd77f95cf16b065f5, 0x14571fea3f49f085, 0x1c333621262b2b3d}},
+ {{0xa86fe55467d9ca81, 0x398b7c752b298c37, 0xda6d0892e3ac623b, 0x4aebcc4547e9d98c}}},
+{{{0x53175a7205d21a77, 0xb0c04422d3b934d4, 0xadd9f24bdd5deadc, 0x074f46e69f10ff8c}},
+ {{0x0de9b204a059a445, 0xe15cb4aa4b17ad0f, 0xe1bbec521f79c557, 0x2633f1b9d071081b}},
+ {{0xc1fb4177018b9910, 0xa6ea20dc6c0fe140, 0xd661f3e74354c6ff, 0x5ecb72e6f1a3407a}}},
+{{{0xa515a31b2259fb4e, 0x0960f3972bcac52f, 0xedb52fec8d3454cb, 0x382e2720c476c019}},
+ {{0xfeeae106e8e86997, 0x9863337f98d09383, 0x9470480eaa06ebef, 0x038b6898d4c5c2d0}},
+ {{0xf391c51d8ace50a6, 0x3142d0b9ae2d2948, 0xdb4d5a1a7f24ca80, 0x21aeba8b59250ea8}}},
+{{{0x24f13b34cf405530, 0x3c44ea4a43088af7, 0x5dd5c5170006a482, 0x118eb8f8890b086d}},
+ {{0x53853600f0087f23, 0x4c461879da7d5784, 0x6af303deb41f6860, 0x0a3c16c5c27c18ed}},
+ {{0x17e49c17cc947f3d, 0xccc6eda6aac1d27b, 0xdf6092ceb0f08e56, 0x4909b3e22c67c36b}}},
+{{{0x9c9c85ea63fe2e89, 0xbe1baf910e9412ec, 0x8f7baa8a86fbfe7b, 0x0fb17f9fef968b6c}},
+ {{0x59a16676706ff64e, 0x10b953dd0d86a53d, 0x5848e1e6ce5c0b96, 0x2d8b78e712780c68}},
+ {{0x79d5c62eafc3902b, 0x773a215289e80728, 0xc38ae640e10120b9, 0x09ae23717b2b1a6d}}},
+{{{0xbb6a192a4e4d083c, 0x34ace0630029e192, 0x98245a59aafabaeb, 0x6d9c8a9ada97faac}},
+ {{0x10ab8fa1ad32b1d0, 0xe9aced1be2778b24, 0xa8856bc0373de90f, 0x66f35ddddda53996}},
+ {{0xd27d9afb24997323, 0x1bb7e07ef6f01d2e, 0x2ba7472df52ecc7f, 0x03019b4f646f9dc8}}},
+{{{0x04a186b5565345cd, 0xeee76610bcc4116a, 0x689c73b478fb2a45, 0x387dcbff65697512}},
+ {{0xaf09b214e6b3dc6b, 0x3f7573b5ad7d2f65, 0xd019d988100a23b0, 0x392b63a58b5c35f7}},
+ {{0x4093addc9c07c205, 0xc565be15f532c37e, 0x63dbecfd1583402a, 0x61722b4aef2e032e}}},
+{{{0x0012aafeecbd47af, 0x55a266fb1cd46309, 0xf203eb680967c72c, 0x39633944ca3c1429}},
+ {{0xd6b07a5581cb0e3c, 0x290ff006d9444969, 0x08680b6a16dcda1f, 0x5568d2b75a06de59}},
+ {{0x8d0cb88c1b37cfe1, 0x05b6a5a3053818f3, 0xf2e9bc04b787d959, 0x6beba1249add7f64}}},
+{{{0x1d06005ca5b1b143, 0x6d4c6bb87fd1cda2, 0x6ef5967653fcffe7, 0x097c29e8c1ce1ea5}},
+ {{0x5c3cecb943f5a53b, 0x9cc9a61d06c08df2, 0xcfba639a85895447, 0x5a845ae80df09fd5}},
+ {{0x4ce97dbe5deb94ca, 0x38d0a4388c709c48, 0xc43eced4a169d097, 0x0a1249fff7e587c3}}},
+{{{0x12f0071b276d01c9, 0xe7b8bac586c48c70, 0x5308129b71d6fba9, 0x5d88fbf95a3db792}},
+ {{0x0b408d9e7354b610, 0x806b32535ba85b6e, 0xdbe63a034a58a207, 0x173bd9ddc9a1df2c}},
+ {{0x2b500f1efe5872df, 0x58d6582ed43918c1, 0xe6ed278ec9673ae0, 0x06e1cd13b19ea319}}},
+{{{0x40d0ad516f166f23, 0x118e32931fab6abe, 0x3fe35e14a04d088e, 0x3080603526e16266}},
+ {{0x472baf629e5b0353, 0x3baa0b90278d0447, 0x0c785f469643bf27, 0x7f3a6a1a8d837b13}},
+ {{0xf7e644395d3d800b, 0x95a8d555c901edf6, 0x68cd7830592c6339, 0x30d0fded2e51307e}}},
+{{{0xe0594d1af21233b3, 0x1bdbe78ef0cc4d9c, 0x6965187f8f499a77, 0x0a9214202c099868}},
+ {{0x9cb4971e68b84750, 0xa09572296664bbcf, 0x5c8de72672fa412b, 0x4615084351c589d9}},
+ {{0xbc9019c0aeb9a02e, 0x55c7110d16034cae, 0x0e6df501659932ec, 0x3bca0d2895ca5dfe}}},
+{{{0x40f031bc3c5d62a4, 0x19fc8b3ecff07a60, 0x98183da2130fb545, 0x5631deddae8f13cd}},
+ {{0x9c688eb69ecc01bf, 0xf0bc83ada644896f, 0xca2d955f5f7a9fe2, 0x4ea8b4038df28241}},
+ {{0x2aed460af1cad202, 0x46305305a48cee83, 0x9121774549f11a5f, 0x24ce0930542ca463}}},
+{{{0x1fe890f5fd06c106, 0xb5c468355d8810f2, 0x827808fe6e8caf3e, 0x41d4e3c28a06d74b}},
+ {{0x3fcfa155fdf30b85, 0xd2f7168e36372ea4, 0xb2e064de6492f844, 0x549928a7324f4280}},
+ {{0xf26e32a763ee1a2e, 0xae91e4b7d25ffdea, 0xbc3bd33bd17f4d69, 0x491b66dec0dcff6a}}},
+{{{0x98f5b13dc7ea32a7, 0xe3d5f8cc7e16db98, 0xac0abf52cbf8d947, 0x08f338d0c85ee4ac}},
+ {{0x75f04a8ed0da64a1, 0xed222caf67e2284b, 0x8234a3791f7b7ba4, 0x4cf6b8b0b7018b67}},
+ {{0xc383a821991a73bd, 0xab27bc01df320c7a, 0xc13d331b84777063, 0x530d4a82eb078a99}}},
+{{{0x004c3630e1f94825, 0x7e2d78268cab535a, 0xc7482323cc84ff8b, 0x65ea753f101770b9}},
+ {{0x6d6973456c9abf9e, 0x257fb2fc4900a880, 0x2bacf412c8cfb850, 0x0db3e7e00cbfbd5b}},
+ {{0x3d66fc3ee2096363, 0x81d62c7f61b5cb6b, 0x0fbe044213443b1a, 0x02a4ec1921e1a1db}}},
+{{{0x5ce6259a3b24b8a2, 0xb8577acc45afa0b8, 0xcccbe6e88ba07037, 0x3d143c51127809bf}},
+ {{0xf5c86162f1cf795f, 0x118c861926ee57f2, 0x172124851c063578, 0x36d12b5dec067fcf}},
+ {{0x126d279179154557, 0xd5e48f5cfc783a0a, 0x36bdb6e8df179bac, 0x2ef517885ba82859}}},
+{{{0x88bd438cd11e0d4a, 0x30cb610d43ccf308, 0xe09a0e3791937bcc, 0x4559135b25b1720c}},
+ {{0x1ea436837c6da1e9, 0xf9c189af1fb9bdbe, 0x303001fcce5dd155, 0x28a7c99ebc57be52}},
+ {{0xb8fd9399e8d19e9d, 0x908191cb962423ff, 0xb2b948d747c742a3, 0x37f33226d7fb44c4}}},
+{{{0x0dae8767b55f6e08, 0x4a43b3b35b203a02, 0xe3725a6e80af8c79, 0x0f7a7fd1705fa7a3}},
+ {{0x33912553c821b11d, 0x66ed42c241e301df, 0x066fcc11104222fd, 0x307a3b41c192168f}},
+ {{0x8eeb5d076eb55ce0, 0x2fc536bfaa0d925a, 0xbe81830fdcb6c6e8, 0x556c7045827baf52}}},
+{{{0x8e2b517302e9d8b7, 0xe3e52269248714e8, 0xbd4fbd774ca960b5, 0x6f4b4199c5ecada9}},
+ {{0xb94b90022bf44406, 0xabd4237eff90b534, 0x7600a960faf86d3a, 0x2f45abdac2322ee3}},
+ {{0x61af4912c8ef8a6a, 0xe58fa4fe43fb6e5e, 0xb5afcc5d6fd427cf, 0x6a5393281e1e11eb}}},
+{{{0xf3da5139a5d1ee89, 0x8145457cff936988, 0x3f622fed00e188c4, 0x0f513815db8b5a3d}},
+ {{0x0fff04fe149443cf, 0x53cac6d9865cddd7, 0x31385b03531ed1b7, 0x5846a27cacd1039d}},
+ {{0x4ff5cdac1eb08717, 0x67e8b29590f2e9bc, 0x44093b5e237afa99, 0x0d414bed8708b8b2}}},
+{{{0xcfb68265fd0e75f6, 0xe45b3e28bb90e707, 0x7242a8de9ff92c7a, 0x685b3201933202dd}},
+ {{0x81886a92294ac9e8, 0x23162b45d55547be, 0x94cfbc4403715983, 0x50eb8fdb134bc401}},
+ {{0xc0b73ec6d6b330cd, 0x84e44807132faff1, 0x732b7352c4a5dee1, 0x5d7c7cf1aa7cd2d2}}},
+{{{0xaf3b46bf7a4aafa2, 0xb78705ec4d40d411, 0x114f0c6aca7c15e3, 0x3f364faaa9489d4d}},
+ {{0x33d1013e9b73a562, 0x925cef5748ec26e1, 0xa7fce614dd468058, 0x78b0fad41e9aa438}},
+ {{0xbf56a431ed05b488, 0xa533e66c9c495c7e, 0xe8652baf87f3651a, 0x0241800059d66c33}}},
+{{{0xceb077fea37a5be4, 0xdb642f02e5a5eeb7, 0xc2e6d0c5471270b8, 0x4771b65538e4529c}},
+ {{0x28350c7dcf38ea01, 0x7c6cdbc0b2917ab6, 0xace7cfbe857082f7, 0x4d2845aba2d9a1e0}},
+ {{0xbb537fe0447070de, 0xcba744436dd557df, 0xd3b5a3473600dbcb, 0x4aeabbe6f9ffd7f8}}},
+{{{0x4630119e40d8f78c, 0xa01a9bc53c710e11, 0x486d2b258910dd79, 0x1e6c47b3db0324e5}},
+ {{0x6a2134bcc4a9c8f2, 0xfbf8fd1c8ace2e37, 0x000ae3049911a0ba, 0x046e3a616bc89b9e}},
+ {{0x14e65442f03906be, 0x4a019d54e362be2a, 0x68ccdfec8dc230c7, 0x7cfb7e3faf6b861c}}},
+{{{0x4637974e8c58aedc, 0xb9ef22fbabf041a4, 0xe185d956e980718a, 0x2f1b78fab143a8a6}},
+ {{0x96eebffb305b2f51, 0xd3f938ad889596b8, 0xf0f52dc746d5dd25, 0x57968290bb3a0095}},
+ {{0xf71ab8430a20e101, 0xf393658d24f0ec47, 0xcf7509a86ee2eed1, 0x7dc43e35dc2aa3e1}}},
+{{{0x85966665887dd9c3, 0xc90f9b314bb05355, 0xc6e08df8ef2079b1, 0x7ef72016758cc12f}},
+ {{0x5a782a5c273e9718, 0x3576c6995e4efd94, 0x0f2ed8051f237d3e, 0x044fb81d82d50a99}},
+ {{0xc1df18c5a907e3d9, 0x57b3371dce4c6359, 0xca704534b201bb49, 0x7f79823f9c30dd2e}}},
+{{{0x8334d239a3b513e8, 0xc13670d4b91fa8d8, 0x12b54136f590bd33, 0x0a4e0373d784d9b4}},
+ {{0x6a9c1ff068f587ba, 0x0827894e0050c8de, 0x3cbf99557ded5be7, 0x64a9b0431c06d6f0}},
+ {{0x2eb3d6a15b7d2919, 0xb0b4f6a0d53a8235, 0x7156ce4389a45d47, 0x071a7d0ace18346c}}},
+{{{0xd3072daac887ba0b, 0x01262905bfa562ee, 0xcf543002c0ef768b, 0x2c3bcc7146ea7e9c}},
+ {{0xcc0c355220e14431, 0x0d65950709b15141, 0x9af5621b209d5f36, 0x7c69bcf7617755d3}},
+ {{0x07f0d7eb04e8295f, 0x10db18252f50f37d, 0xe951a9a3171798d7, 0x6f5a9a7322aca51d}}},
+{{{0x8ba1000c2f41c6c5, 0xc49f79c10cfefb9b, 0x4efa47703cc51c9f, 0x494e21a2e147afca}},
+ {{0xe729d4eba3d944be, 0x8d9e09408078af9e, 0x4525567a47869c03, 0x02ab9680ee8d3b24}},
+ {{0xefa48a85dde50d9a, 0x219a224e0fb9a249, 0xfa091f1dd91ef6d9, 0x6b5d76cbea46bb34}}},
+{{{0x8857556cec0cd994, 0x6472dc6f5cd01dba, 0xaf0169148f42b477, 0x0ae333f685277354}},
+ {{0xe0f941171e782522, 0xf1e6ae74036936d3, 0x408b3ea2d0fcc746, 0x16fb869c03dd313e}},
+ {{0x288e199733b60962, 0x24fc72b4d8abe133, 0x4811f7ed0991d03e, 0x3f81e38b8f70d075}}},
+{{{0x7f910fcc7ed9affe, 0x545cb8a12465874b, 0xa8397ed24b0c4704, 0x50510fc104f50993}},
+ {{0x0adb7f355f17c824, 0x74b923c3d74299a4, 0xd57c3e8bcbf8eaf7, 0x0ad3e2d34cdedc3d}},
+ {{0x6f0c0fc5336e249d, 0x745ede19c331cfd9, 0xf2d6fd0009eefe1c, 0x127c158bf0fa1ebe}}},
+{{{0xf6197c422e9879a2, 0xa44addd452ca3647, 0x9b413fc14b4eaccb, 0x354ef87d07ef4f68}},
+ {{0xdea28fc4ae51b974, 0x1d9973d3744dfe96, 0x6240680b873848a8, 0x4ed82479d167df95}},
+ {{0xfee3b52260c5d975, 0x50352efceb41b0b8, 0x8808ac30a9f6653c, 0x302d92d20539236d}}},
+{{{0x4c59023fcb3efb7c, 0x6c2fcb99c63c2a94, 0xba4190e2c3c7e084, 0x0e545daea51874d9}},
+ {{0x957b8b8b0df53c30, 0x2a1c770a8e60f098, 0xbbc7a670345796de, 0x22a48f9a90c99bc9}},
+ {{0x6b7dc0dc8d3fac58, 0x5497cd6ce6e42bfd, 0x542f7d1bf400d305, 0x4159f47f048d9136}}},
+{{{0x20ad660839e31e32, 0xf81e1bd58405be50, 0xf8064056f4dabc69, 0x14d23dd4ce71b975}},
+ {{0x748515a8bbd24839, 0x77128347afb02b55, 0x50ba2ac649a2a17f, 0x060525513ad730f1}},
+ {{0xf2398e098aa27f82, 0x6d7982bb89a1b024, 0xfa694084214dd24c, 0x71ab966fa32301c3}}},
+{{{0x2dcbd8e34ded02fc, 0x1151f3ec596f22aa, 0xbca255434e0328da, 0x35768fbe92411b22}},
+ {{0xb1088a0702809955, 0x43b273ea0b43c391, 0xca9b67aefe0686ed, 0x605eecbf8335f4ed}},
+ {{0x83200a656c340431, 0x9fcd71678ee59c2f, 0x75d4613f71300f8a, 0x7a912faf60f542f9}}},
+{{{0xb204585e5edc1a43, 0x9f0e16ee5897c73c, 0x5b82c0ae4e70483c, 0x624a170e2bddf9be}},
+ {{0x253f4f8dfa2d5597, 0x25e49c405477130c, 0x00c052e5996b1102, 0x33cb966e33bb6c4a}},
+ {{0x597028047f116909, 0x828ac41c1e564467, 0x70417dbde6217387, 0x721627aefbac4384}}},
+{{{0x97d03bc38736add5, 0x2f1422afc532b130, 0x3aa68a057101bbc4, 0x4c946cf7e74f9fa7}},
+ {{0xfd3097bc410b2f22, 0xf1a05da7b5cfa844, 0x61289a1def57ca74, 0x245ea199bb821902}},
+ {{0xaedca66978d477f8, 0x1898ba3c29117fe1, 0xcf73f983720cbd58, 0x67da12e6b8b56351}}},
+{{{0x7067e187b4bd6e07, 0x6e8f0203c7d1fe74, 0x93c6aa2f38c85a30, 0x76297d1f3d75a78a}},
+ {{0x2b7ef3d38ec8308c, 0x828fd7ec71eb94ab, 0x807c3b36c5062abd, 0x0cb64cb831a94141}},
+ {{0x3030fc33534c6378, 0xb9635c5ce541e861, 0x15d9a9bed9b2c728, 0x49233ea3f3775dcb}}},
+{{{0x629398fa8dbffc3a, 0xe12fe52dd54db455, 0xf3be11dfdaf25295, 0x628b140dce5e7b51}},
+ {{0x7b3985fe1c9f249b, 0x4fd6b2d5a1233293, 0xceb345941adf4d62, 0x6987ff6f542de50c}},
+ {{0x47e241428f83753c, 0x6317bebc866af997, 0xdabb5b433d1a9829, 0x074d8d245287fb2d}}},
+{{{0x8337d9cd440bfc31, 0x729d2ca1af318fd7, 0xa040a4a4772c2070, 0x46002ef03a7349be}},
+ {{0x481875c6c0e31488, 0x219429b2e22034b4, 0x7223c98a31283b65, 0x3420d60b342277f9}},
+ {{0xfaa23adeaffe65f7, 0x78261ed45be0764c, 0x441c0a1e2f164403, 0x5aea8e567a87d395}}},
+{{{0x7813c1a2bca4283d, 0xed62f091a1863dd9, 0xaec7bcb8c268fa86, 0x10e5d3b76f1cae4c}},
+ {{0x2dbc6fb6e4e0f177, 0x04e1bf29a4bd6a93, 0x5e1966d4787af6e8, 0x0edc5f5eb426d060}},
+ {{0x5453bfd653da8e67, 0xe9dc1eec24a9f641, 0xbf87263b03578a23, 0x45b46c51361cba72}}},
+{{{0xa9402abf314f7fa1, 0xe257f1dc8e8cf450, 0x1dbbd54b23a8be84, 0x2177bfa36dcb713b}},
+ {{0xce9d4ddd8a7fe3e4, 0xab13645676620e30, 0x4b594f7bb30e9958, 0x5c1c0aef321229df}},
+ {{0x37081bbcfa79db8f, 0x6048811ec25f59b3, 0x087a76659c832487, 0x4ae619387d8ab5bb}}},
+{{{0x8ddbf6aa5344a32e, 0x7d88eab4b41b4078, 0x5eb0eb974a130d60, 0x1a00d91b17bf3e03}},
+ {{0x61117e44985bfb83, 0xfce0462a71963136, 0x83ac3448d425904b, 0x75685abe5ba43d64}},
+ {{0x6e960933eb61f2b2, 0x543d0fa8c9ff4952, 0xdf7275107af66569, 0x135529b623b0e6aa}}},
+{{{0x18f0dbd7add1d518, 0x979f7888cfc11f11, 0x8732e1f07114759b, 0x79b5b81a65ca3a01}},
+ {{0xf5c716bce22e83fe, 0xb42beb19e80985c1, 0xec9da63714254aae, 0x5972ea051590a613}},
+ {{0x0fd4ac20dc8f7811, 0x9a9ad294ac4d4fa8, 0xc01b2d64b3360434, 0x4f7e9c95905f3bdb}}},
+{{{0x62674bbc5781302e, 0xd8520f3989addc0f, 0x8c2999ae53fbd9c6, 0x31993ad92e638e4c}},
+ {{0x71c8443d355299fe, 0x8bcd3b1cdbebead7, 0x8092499ef1a49466, 0x1942eec4a144adc8}},
+ {{0x7dac5319ae234992, 0x2c1b3d910cea3e92, 0x553ce494253c1122, 0x2a0a65314ef9ca75}}},
+{{{0x2db7937ff7f927c2, 0xdb741f0617d0a635, 0x5982f3a21155af76, 0x4cf6e218647c2ded}},
+ {{0xcf361acd3c1c793a, 0x2f9ebcac5a35bc3b, 0x60e860e9a8cda6ab, 0x055dc39b6dea1a13}},
+ {{0xb119227cc28d5bb6, 0x07e24ebc774dffab, 0xa83c78cee4a32c89, 0x121a307710aa24b6}}},
+{{{0xe4db5d5e9f034a97, 0xe153fc093034bc2d, 0x460546919551d3b1, 0x333fc76c7a40e52d}},
+ {{0xd659713ec77483c9, 0x88bfe077b82b96af, 0x289e28231097bcd3, 0x527bb94a6ced3a9b}},
+ {{0x563d992a995b482e, 0x3405d07c6e383801, 0x485035de2f64d8e5, 0x6b89069b20a7a9f7}}},
+{{{0x812aa0416270220d, 0x995a89faf9245b4e, 0xffadc4ce5072ef05, 0x23bc2103aa73eb73}},
+ {{0x4082fa8cb5c7db77, 0x068686f8c734c155, 0x29e6c8d9f6e7a57e, 0x0473d308a7639bcf}},
+ {{0xcaee792603589e05, 0x2b4b421246dcc492, 0x02a1ef74e601a94f, 0x102f73bfde04341a}}},
+{{{0xb5a2d50c7ec20d3e, 0xc64bdd6ea0c97263, 0x56e89052c1ff734d, 0x4929c6f72b2ffaba}},
+ {{0x358ecba293a36247, 0xaf8f9862b268fd65, 0x412f7e9968a01c89, 0x5786f312cd754524}},
+ {{0x337788ffca14032c, 0xf3921028447f1ee3, 0x8b14071f231bccad, 0x4c817b4bf2344783}}},
+{{{0x0ff853852871b96e, 0xe13e9fab60c3f1bb, 0xeefd595325344402, 0x0a37c37075b7744b}},
+ {{0x413ba057a40b4484, 0xba4c2e1a4f5f6a43, 0x614ba0a5aee1d61c, 0x78a1531a8b05dc53}},
+ {{0x6cbdf1703ad0562b, 0x8ecf4830c92521a3, 0xdaebd303fd8424e7, 0x72ad82a42e5ec56f}}},
+{{{0x3f9e8e35bafb65f6, 0x39d69ec8f27293a1, 0x6cb8cd958cf6a3d0, 0x1734778173adae6d}},
+ {{0xc368939167024bc3, 0x8e69d16d49502fda, 0xfcf2ec3ce45f4b29, 0x065f669ea3b4cbc4}},
+ {{0x8a00aec75532db4d, 0xb869a4e443e31bb1, 0x4a0f8552d3a7f515, 0x19adeb7c303d7c08}}},
+{{{0xc720cb6153ead9a3, 0x55b2c97f512b636e, 0xb1e35b5fd40290b1, 0x2fd9ccf13b530ee2}},
+ {{0x9d05ba7d43c31794, 0x2470c8ff93322526, 0x8323dec816197438, 0x2852709881569b53}},
+ {{0x07bd475b47f796b8, 0xd2c7b013542c8f54, 0x2dbd23f43b24f87e, 0x6551afd77b0901d6}}},
+{{{0x4546baaf54aac27f, 0xf6f66fecb2a45a28, 0x582d1b5b562bcfe8, 0x44b123f3920f785f}},
+ {{0x68a24ce3a1d5c9ac, 0xbb77a33d10ff6461, 0x0f86ce4425d3166e, 0x56507c0950b9623b}},
+ {{0x1206f0b7d1713e63, 0x353fe3d915bafc74, 0x194ceb970ad9d94d, 0x62fadd7cf9d03ad3}}},
+{{{0xc6b5967b5598a074, 0x5efe91ce8e493e25, 0xd4b72c4549280888, 0x20ef1149a26740c2}},
+ {{0x3cd7bc61e7ce4594, 0xcd6b35a9b7dd267e, 0xa080abc84366ef27, 0x6ec7c46f59c79711}},
+ {{0x2f07ad636f09a8a2, 0x8697e6ce24205e7d, 0xc0aefc05ee35a139, 0x15e80958b5f9d897}}},
+{{{0x25a5ef7d0c3e235b, 0x6c39c17fbe134ee7, 0xc774e1342dc5c327, 0x021354b892021f39}},
+ {{0x4dd1ed355bb061c4, 0x42dc0cef941c0700, 0x61305dc1fd86340e, 0x56b2cc930e55a443}},
+ {{0x1df79da6a6bfc5a2, 0x02f3a2749fde4369, 0xb323d9f2cda390a7, 0x7be0847b8774d363}}},
+{{{0x8c99cc5a8b3f55c3, 0x0611d7253fded2a0, 0xed2995ff36b70a36, 0x1f699a54d78a2619}},
+ {{0x1466f5af5307fa11, 0x817fcc7ded6c0af2, 0x0a6de44ec3a4a3fb, 0x74071475bc927d0b}},
+ {{0xe77292f373e7ea8a, 0x296537d2cb045a31, 0x1bd0653ed3274fde, 0x2f9a2c4476bd2966}}},
+{{{0xeb18b9ab7f5745c6, 0x023a8aee5787c690, 0xb72712da2df7afa9, 0x36597d25ea5c013d}},
+ {{0xa2b4dae0b5511c9a, 0x7ac860292bffff06, 0x981f375df5504234, 0x3f6bd725da4ea12d}},
+ {{0x734d8d7b106058ac, 0xd940579e6fc6905f, 0x6466f8f99202932d, 0x7b7ecc19da60d6d0}}},
+{{{0x78c2373c695c690d, 0xdd252e660642906e, 0x951d44444ae12bd2, 0x4235ad7601743956}},
+ {{0x6dae4a51a77cfa9b, 0x82263654e7a38650, 0x09bbffcd8f2d82db, 0x03bedc661bf5caba}},
+ {{0x6258cb0d078975f5, 0x492942549189f298, 0xa0cab423e2e36ee4, 0x0e7ce2b0cdf066a1}}},
+{{{0xc494643ac48c85a3, 0xfd361df43c6139ad, 0x09db17dd3ae94d48, 0x666e0a5d8fb4674a}},
+ {{0xfea6fedfd94b70f9, 0xf130c051c1fcba2d, 0x4882d47e7f2fab89, 0x615256138aeceeb5}},
+ {{0x2abbf64e4870cb0d, 0xcd65bcf0aa458b6b, 0x9abe4eba75e8985d, 0x7f0bc810d514dee4}}},
+{{{0xb9006ba426f4136f, 0x8d67369e57e03035, 0xcbc8dfd94f463c28, 0x0d1f8dbcf8eedbf5}},
+ {{0x83ac9dad737213a0, 0x9ff6f8ba2ef72e98, 0x311e2edd43ec6957, 0x1d3a907ddec5ab75}},
+ {{0xba1693313ed081dc, 0x29329fad851b3480, 0x0128013c030321cb, 0x00011b44a31bfde3}}},
+{{{0x3fdfa06c3fc66c0c, 0x5d40e38e4dd60dd2, 0x7ae38b38268e4d71, 0x3ac48d916e8357e1}},
+ {{0x16561f696a0aa75c, 0xc1bf725c5852bd6a, 0x11a8dd7f9a7966ad, 0x63d988a2d2851026}},
+ {{0x00120753afbd232e, 0xe92bceb8fdd8f683, 0xf81669b384e72b91, 0x33fad52b2368a066}}},
+{{{0x540649c6c5e41e16, 0x0af86430333f7735, 0xb2acfcd2f305e746, 0x16c0f429a256dca7}},
+ {{0x8d2cc8d0c422cfe8, 0x072b4f7b05a13acb, 0xa3feb6e6ecf6a56f, 0x3cc355ccb90a71e2}},
+ {{0xe9b69443903e9131, 0xb8a494cb7a5637ce, 0xc87cd1a4baba9244, 0x631eaf426bae7568}}},
+{{{0xb3e90410da66fe9f, 0x85dd4b526c16e5a6, 0xbc3d97611ef9bf83, 0x5599648b1ea919b5}},
+ {{0x47d975b9a3700de8, 0x7280c5fbe2f80552, 0x53658f2732e45de1, 0x431f2c7f665f80b5}},
+ {{0xd6026344858f7b19, 0x14ab352fa1ea514a, 0x8900441a2090a9d7, 0x7b04715f91253b26}}},
+{{{0x83edbd28acf6ae43, 0x86357c8b7d5c7ab4, 0xc0404769b7eb2c44, 0x59b37bf5c2f6583f}},
+ {{0xb376c280c4e6bac6, 0x970ed3dd6d1d9b0b, 0xb09a9558450bf944, 0x48d0acfa57cde223}},
+ {{0xb60f26e47dabe671, 0xf1d1a197622f3a37, 0x4208ce7ee9960394, 0x16234191336d3bdb}}},
+{{{0xf19aeac733a63aef, 0x2c7fba5d4442454e, 0x5da87aa04795e441, 0x413051e1a4e0b0f5}},
+ {{0x852dd1fd3d578bbe, 0x2b65ce72c3286108, 0x658c07f4eace2273, 0x0933f804ec38ab40}},
+ {{0xa7ab69798d496476, 0x8121aadefcb5abc8, 0xa5dc12ef7b539472, 0x07fd47065e45351a}}},
+{{{0xc8583c3d258d2bcd, 0x17029a4daf60b73f, 0xfa0fc9d6416a3781, 0x1c1e5fba38b3fb23}},
+ {{0x304211559ae8e7c3, 0xf281b229944882a5, 0x8a13ac2e378250e4, 0x014afa0954ba48f4}},
+ {{0xcb3197001bb3666c, 0x330060524bffecb9, 0x293711991a88233c, 0x291884363d4ed364}}},
+{{{0x033c6805dc4babfa, 0x2c15bf5e5596ecc1, 0x1bc70624b59b1d3b, 0x3ede9850a19f0ec5}},
+ {{0xfb9d37c3bc1ab6eb, 0x02be14534d57a240, 0xf4d73415f8a5e1f6, 0x5964f4300ccc8188}},
+ {{0xe44a23152d096800, 0x5c08c55970866996, 0xdf2db60a46affb6e, 0x579155c1f856fd89}}},
+{{{0x96324edd12e0c9ef, 0x468b878df2420297, 0x199a3776a4f573be, 0x1e7fbcf18e91e92a}},
+ {{0xb5f16b630817e7a6, 0x808c69233c351026, 0x324a983b54cef201, 0x53c092084a485345}},
+ {{0xd2d41481f1cbafbf, 0x231d2db6716174e5, 0x0b7d7656e2a55c98, 0x3e955cd82aa495f6}}},
+{{{0xe48f535e3ed15433, 0xd075692a0d7270a3, 0x40fbd21daade6387, 0x14264887cf4495f5}},
+ {{0xab39f3ef61bb3a3f, 0x8eb400652eb9193e, 0xb5de6ecc38c11f74, 0x654d7e9626f3c49f}},
+ {{0xe564cfdd5c7d2ceb, 0x82eeafded737ccb9, 0x6107db62d1f9b0ab, 0x0b6baac3b4358dbb}}},
+{{{0x7ae62bcb8622fe98, 0x47762256ceb891af, 0x1a5a92bcf2e406b4, 0x7d29401784e41501}},
+ {{0x204abad63700a93b, 0xbe0023d3da779373, 0xd85f0346633ab709, 0x00496dc490820412}},
+ {{0x1c74b88dc27e6360, 0x074854268d14850c, 0xa145fb7b3e0dcb30, 0x10843f1b43803b23}}},
+{{{0xc5f90455376276dd, 0xce59158dd7645cd9, 0x92f65d511d366b39, 0x11574b6e526996c4}},
+ {{0xd56f672de324689b, 0xd1da8aedb394a981, 0xdd7b58fe9168cfed, 0x7ce246cd4d56c1e8}},
+ {{0xb8f4308e7f80be53, 0x5f3cb8cb34a9d397, 0x18a961bd33cc2b2c, 0x710045fb3a9af671}}},
+{{{0x73f93d36101b95eb, 0xfaef33794f6f4486, 0x5651735f8f15e562, 0x7fa3f19058b40da1}},
+ {{0xa03fc862059d699e, 0x2370cfa19a619e69, 0xc4fe3b122f823deb, 0x1d1b056fa7f0844e}},
+ {{0x1bc64631e56bf61f, 0xd379ab106e5382a3, 0x4d58c57e0540168d, 0x566256628442d8e4}}},
+{{{0xb9e499def6267ff6, 0x7772ca7b742c0843, 0x23a0153fe9a4f2b1, 0x2cdfdfecd5d05006}},
+ {{0xdd499cd61ff38640, 0x29cd9bc3063625a0, 0x51e2d8023dd73dc3, 0x4a25707a203b9231}},
+ {{0x2ab7668a53f6ed6a, 0x304242581dd170a1, 0x4000144c3ae20161, 0x5721896d248e49fc}}},
+{{{0x0b6e5517fd181bae, 0x9022629f2bb963b4, 0x5509bce932064625, 0x578edd74f63c13da}},
+ {{0x285d5091a1d0da4e, 0x4baa6fa7b5fe3e08, 0x63e5177ce19393b3, 0x03c935afc4b030fd}},
+ {{0x997276c6492b0c3d, 0x47ccc2c4dfe205fc, 0xdcd29b84dd623a3c, 0x3ec2ab590288c7a2}}},
+{{{0xa1a0d27be4d87bb9, 0xa98b4deb61391aed, 0x99a0ddd073cb9b83, 0x2dd5c25a200fcace}},
+ {{0xa7213a09ae32d1cb, 0x0f2b87df40f5c2d5, 0x0baea4c6e81eab29, 0x0e1bf66c6adbac5e}},
+ {{0xe2abd5e9792c887e, 0x1a020018cb926d5d, 0xbfba69cdbaae5f1e, 0x730548b35ae88f5f}}},
+{{{0xc43551a3cba8b8ee, 0x65a26f1db2115f16, 0x760f4f52ab8c3850, 0x3043443b411db8ca}},
+ {{0x805b094ba1d6e334, 0xbf3ef17709353f19, 0x423f06cb0622702b, 0x585a2277d87845dd}},
+ {{0xa18a5f8233d48962, 0x6698c4b5ec78257f, 0xa78e6fa5373e41ff, 0x7656278950ef981f}}},
+{{{0x38c3cf59d51fc8c0, 0x9bedd2fd0506b6f2, 0x26bf109fab570e8f, 0x3f4160a8c1b846a6}},
+ {{0xe17073a3ea86cf9d, 0x3a8cfbb707155fdc, 0x4853e7fc31838a8e, 0x28bbf484b613f616}},
+ {{0xf2612f5c6f136c7c, 0xafead107f6dd11be, 0x527e9ad213de6f33, 0x1e79cb358188f75d}}},
+{{{0x013436c3eef7e3f1, 0x828b6a7ffe9e10f8, 0x7ff908e5bcf9defc, 0x65d7951b3a3b3831}},
+ {{0x77e953d8f5e08181, 0x84a50c44299dded9, 0xdc6c2d0c864525e5, 0x478ab52d39d1f2f4}},
+ {{0x66a6a4d39252d159, 0xe5dde1bc871ac807, 0xb82c6b40a6c1c96f, 0x16d87a411a212214}}},
+{{{0xb3bd7e5a42066215, 0x879be3cd0c5a24c1, 0x57c05db1d6f994b7, 0x28f87c8165f38ca6}},
+ {{0xfba4d5e2d54e0583, 0xe21fafd72ebd99fa, 0x497ac2736ee9778f, 0x1f990b577a5a6dde}},
+ {{0xa3344ead1be8f7d6, 0x7d1e50ebacea798f, 0x77c6569e520de052, 0x45882fe1534d6d3e}}},
+{{{0x6669345d757983d6, 0x62b6ed1117aa11a6, 0x7ddd1857985e128f, 0x688fe5b8f626f6dd}},
+ {{0xd8ac9929943c6fe4, 0xb5f9f161a38392a2, 0x2699db13bec89af3, 0x7dcf843ce405f074}},
+ {{0x6c90d6484a4732c0, 0xd52143fdca563299, 0xb3be28c3915dc6e1, 0x6739687e7327191b}}},
+{{{0x9f65c5ea200814cf, 0x840536e169a31740, 0x8b0ed13925c8b4ad, 0x0080dbafe936361d}},
+ {{0x8ce5aad0c9cb971f, 0x1156aaa99fd54a29, 0x41f7247015af9b78, 0x1fe8cca8420f49aa}},
+ {{0x72a1848f3c0cc82a, 0x38c560c2877c9e54, 0x5004e228ce554140, 0x042418a103429d71}}},
+{{{0x899dea51abf3ff5f, 0x9b93a8672fc2d8ba, 0x2c38cb97be6ebd5c, 0x114d578497263b5d}},
+ {{0x58e84c6f20816247, 0x8db2b2b6e36fd793, 0x977182561d484d85, 0x0822024f8632abd7}},
+ {{0xb301bb7c6b1beca3, 0x55393f6dc6eb1375, 0x910d281097b6e4eb, 0x1ad4548d9d479ea3}}},
+{{{0xcd5a7da0389a48fd, 0xb38fa4aa9a78371e, 0xc6d9761b2cdb8e6c, 0x35cf51dbc97e1443}},
+ {{0xa06fe66d0fe9fed3, 0xa8733a401c587909, 0x30d14d800df98953, 0x41ce5876c7b30258}},
+ {{0x59ac3bc5d670c022, 0xeae67c109b119406, 0x9798bdf0b3782fda, 0x651e3201fd074092}}},
+{{{0xd63d8483ef30c5cf, 0x4cd4b4962361cc0c, 0xee90e500a48426ac, 0x0af51d7d18c14eeb}},
+ {{0xa57ba4a01efcae9e, 0x769f4beedc308a94, 0xd1f10eeb3603cb2e, 0x4099ce5e7e441278}},
+ {{0x1ac98e4f8a5121e9, 0x7dae9544dbfa2fe0, 0x8320aa0dd6430df9, 0x667282652c4a2fb5}}},
+{{{0x874621f4d86bc9ab, 0xb54c7bbe56fe6fea, 0x077a24257fadc22c, 0x1ab53be419b90d39}},
+ {{0xada8b6e02946db23, 0x1c0ce51a7b253ab7, 0x8448c85a66dd485b, 0x7f1fc025d0675adf}},
+ {{0xd8ee1b18319ea6aa, 0x004d88083a21f0da, 0x3bd6aa1d883a4f4b, 0x4db9a3a6dfd9fd14}}},
+{{{0x8ce7b23bb99c0755, 0x35c5d6edc4f50f7a, 0x7e1e2ed2ed9b50c3, 0x36305f16e8934da1}},
+ {{0xd95b00bbcbb77c68, 0xddbc846a91f17849, 0x7cf700aebe28d9b3, 0x5ce1285c85d31f3e}},
+ {{0x31b6972d98b0bde8, 0x7d920706aca6de5b, 0xe67310f8908a659f, 0x50fac2a6efdf0235}}},
+{{{0xf3d3a9f35b880f5a, 0xedec050cdb03e7c2, 0xa896981ff9f0b1a2, 0x49a4ae2bac5e34a4}},
+ {{0x295b1c86f6f449bc, 0x51b2e84a1f0ab4dd, 0xc001cb30aa8e551d, 0x6a28d35944f43662}},
+ {{0x28bb12ee04a740e0, 0x14313bbd9bce8174, 0x72f5b5e4e8c10c40, 0x7cbfb19936adcd5b}}},
+{{{0xa311ddc26b89792d, 0x1b30b4c6da512664, 0x0ca77b4ccf150859, 0x1de443df1b009408}},
+ {{0x8e793a7acc36e6e0, 0xf9fab7a37d586eed, 0x3a4f9692bae1f4e4, 0x1c14b03eff5f447e}},
+ {{0x19647bd114a85291, 0x57b76cb21034d3af, 0x6329db440f9d6dfa, 0x5ef43e586a571493}}},
+{{{0xef782014385675a6, 0xa2649f30aafda9e8, 0x4cd1eb505cdfa8cb, 0x46115aba1d4dc0b3}},
+ {{0xa66dcc9dc80c1ac0, 0x97a05cf41b38a436, 0xa7ebf3be95dbd7c6, 0x7da0b8f68d7e7dab}},
+ {{0xd40f1953c3b5da76, 0x1dac6f7321119e9b, 0x03cc6021feb25960, 0x5a5f887e83674b4b}}},
+{{{0x8f6301cf70a13d11, 0xcfceb815350dd0c4, 0xf70297d4a4bca47e, 0x3669b656e44d1434}},
+ {{0x9e9628d3a0a643b9, 0xb5c3cb00e6c32064, 0x9b5302897c2dec32, 0x43e37ae2d5d1c70c}},
+ {{0x387e3f06eda6e133, 0x67301d5199a13ac0, 0xbd5ad8f836263811, 0x6a21e6cd4fd5e9be}}},
+{{{0xf1c6170a3046e65f, 0x58712a2a00d23524, 0x69dbbd3c8c82b755, 0x586bf9f1a195ff57}},
+ {{0xef4129126699b2e3, 0x71d30847708d1301, 0x325432d01182b0bd, 0x45371b07001e8b36}},
+ {{0xa6db088d5ef8790b, 0x5278f0dc610937e5, 0xac0349d261a16eb8, 0x0eafb03790e52179}}},
+{{{0x960555c13748042f, 0x219a41e6820baa11, 0x1c81f73873486d0c, 0x309acc675a02c661}},
+ {{0x5140805e0f75ae1d, 0xec02fbe32662cc30, 0x2cebdf1eea92396d, 0x44ae3344c5435bb3}},
+ {{0x9cf289b9bba543ee, 0xf3760e9d5ac97142, 0x1d82e5c64f9360aa, 0x62d5221b7f94678f}}},
+{{{0x524c299c18d0936d, 0xc86bb56c8a0c1a0c, 0xa375052edb4a8631, 0x5c0efde4bc754562}},
+ {{0x7585d4263af77a3c, 0xdfae7b11fee9144d, 0xa506708059f7193d, 0x14f29a5383922037}},
+ {{0xdf717edc25b2d7f5, 0x21f970db99b53040, 0xda9234b7c3ed4c62, 0x5e72365c7bee093e}}},
+{{{0x575bfc074571217f, 0x3779675d0694d95b, 0x9a0a37bbf4191e33, 0x77f1104c47b4eabc}},
+ {{0x7d9339062f08b33e, 0x5b9659e5df9f32be, 0xacff3dad1f9ebdfd, 0x70b20555cb7349b7}},
+ {{0xbe5113c555112c4c, 0x6688423a9a881fcd, 0x446677855e503b47, 0x0e34398f4a06404a}}},
+{{{0xb67d22d93ecebde8, 0x09b3e84127822f07, 0x743fa61fb05b6d8d, 0x5e5405368a362372}},
+ {{0x18930b093e4b1928, 0x7de3e10e73f3f640, 0xf43217da73395d6f, 0x6f8aded6ca379c3e}},
+ {{0xe340123dfdb7b29a, 0x487b97e1a21ab291, 0xf9967d02fde6949e, 0x780de72ec8d3de97}}},
+{{{0x0ae28545089ae7bc, 0x388ddecf1c7f4d06, 0x38ac15510a4811b8, 0x0eb28bf671928ce4}},
+ {{0x671feaf300f42772, 0x8f72eb2a2a8c41aa, 0x29a17fd797373292, 0x1defc6ad32b587a6}},
+ {{0xaf5bbe1aef5195a7, 0x148c1277917b15ed, 0x2991f7fb7ae5da2e, 0x467d201bf8dd2867}}},
+{{{0x7906ee72f7bd2e6b, 0x05d270d6109abf4e, 0x8d5cfe45b941a8a4, 0x44c218671c974287}},
+ {{0x745f9d56296bc318, 0x993580d4d8152e65, 0xb0e5b13f5839e9ce, 0x51fc2b28d43921c0}},
+ {{0x1b8fd11795e2a98c, 0x1c4e5ee12b6b6291, 0x5b30e7107424b572, 0x6e6b9de84c4f4ac6}}},
+{{{0xdff25fce4b1de151, 0xd841c0c7e11c4025, 0x2554b3c854749c87, 0x2d292459908e0df9}},
+ {{0x6b7c5f10f80cb088, 0x736b54dc56e42151, 0xc2b620a5c6ef99c4, 0x5f4c802cc3a06f42}},
+ {{0x9b65c8f17d0752da, 0x881ce338c77ee800, 0xc3b514f05b62f9e3, 0x66ed5dd5bec10d48}}},
+{{{0x7d38a1c20bb2089d, 0x808334e196ccd412, 0xc4a70b8c6c97d313, 0x2eacf8bc03007f20}},
+ {{0xf0adf3c9cbca047d, 0x81c3b2cbf4552f6b, 0xcfda112d44735f93, 0x1f23a0c77e20048c}},
+ {{0xf235467be5bc1570, 0x03d2d9020dbab38c, 0x27529aa2fcf9e09e, 0x0840bef29d34bc50}}},
+{{{0x796dfb35dc10b287, 0x27176bcd5c7ff29d, 0x7f3d43e8c7b24905, 0x0304f5a191c54276}},
+ {{0xcd54e06b7f37e4eb, 0x8cc15f87f5e96cca, 0xb8248bb0d3597dce, 0x246affa06074400c}},
+ {{0x37d88e68fbe45321, 0x86097548c0d75032, 0x4e9b13ef894a0d35, 0x25a83cac5753d325}}},
+{{{0x10222f48eed8165e, 0x623fc1234b8bcf3a, 0x1e145c09c221e8f0, 0x7ccfa59fca782630}},
+ {{0x9f0f66293952b6e2, 0x33db5e0e0934267b, 0xff45252bd609fedc, 0x06be10f5c506e0c9}},
+ {{0x1a9615a9b62a345f, 0x22050c564a52fecc, 0xa7a2788528bc0dfe, 0x5e82770a1a1ee71d}}},
+{{{0x35425183ad896a5c, 0xe8673afbe78d52f6, 0x2c66f25f92a35f64, 0x09d04f3b3b86b102}},
+ {{0xe802e80a42339c74, 0x34175166a7fffae5, 0x34865d1f1c408cae, 0x2cca982c605bc5ee}},
+ {{0xfd2d5d35197dbe6e, 0x207c2eea8be4ffa3, 0x2613d8db325ae918, 0x7a325d1727741d3e}}},
+{{{0xd036b9bbd16dfde2, 0xa2055757c497a829, 0x8e6cc966a7f12667, 0x4d3b1a791239c180}},
+ {{0xecd27d017e2a076a, 0xd788689f1636495e, 0x52a61af0919233e5, 0x2a479df17bb1ae64}},
+ {{0x9e5eee8e33db2710, 0x189854ded6c43ca5, 0xa41c22c592718138, 0x27ad5538a43a5e9b}}},
+{{{0x2746dd4b15350d61, 0xd03fcbc8ee9521b7, 0xe86e365a138672ca, 0x510e987f7e7d89e2}},
+ {{0xcb5a7d638e47077c, 0x8db7536120a1c059, 0x549e1e4d8bedfdcc, 0x080153b7503b179d}},
+ {{0xdda69d930a3ed3e3, 0x3d386ef1cd60a722, 0xc817ad58bdaa4ee6, 0x23be8d554fe7372a}}},
+{{{0x95fe919a74ef4fad, 0x3a827becf6a308a2, 0x964e01d309a47b01, 0x71c43c4f5ba3c797}},
+ {{0xbc1ef4bd567ae7a9, 0x3f624cb2d64498bd, 0xe41064d22c1f4ec8, 0x2ef9c5a5ba384001}},
+ {{0xb6fd6df6fa9e74cd, 0xf18278bce4af267a, 0x8255b3d0f1ef990e, 0x5a758ca390c5f293}}},
+{{{0xa2b72710d9462495, 0x3aa8c6d2d57d5003, 0xe3d400bfa0b487ca, 0x2dbae244b3eb72ec}},
+ {{0x8ce0918b1d61dc94, 0x8ded36469a813066, 0xd4e6a829afe8aad3, 0x0a738027f639d43f}},
+ {{0x980f4a2f57ffe1cc, 0x00670d0de1839843, 0x105c3f4a49fb15fd, 0x2698ca635126a69c}}},
+{{{0xe765318832b0ba78, 0x381831f7925cff8b, 0x08a81b91a0291fcc, 0x1fb43dcc49caeb07}},
+ {{0x2e3d702f5e3dd90e, 0x9e3f0918e4d25386, 0x5e773ef6024da96a, 0x3c004b0c4afa3332}},
+ {{0x9aa946ac06f4b82b, 0x1ca284a5a806c4f3, 0x3ed3265fc6cd4787, 0x6b43fd01cd1fd217}}},
+{{{0xc7a75d4b4697c544, 0x15fdf848df0fffbf, 0x2868b9ebaa46785a, 0x5a68d7105b52f714}},
+ {{0xb5c742583e760ef3, 0x75dc52b9ee0ab990, 0xbf1427c2072b923f, 0x73420b2d6ff0d9f0}},
+ {{0xaf2cf6cb9e851e06, 0x8f593913c62238c4, 0xda8ab89699fbf373, 0x3db5632fea34bc9e}}},
+{{{0xf46eee2bf75dd9d8, 0x0d17b1f6396759a5, 0x1bf2d131499e7273, 0x04321adf49d75f13}},
+ {{0x2e4990b1829825d5, 0xedeaeb873e9a8991, 0xeef03d394c704af8, 0x59197ea495df2b0e}},
+ {{0x04e16019e4e55aae, 0xe77b437a7e2f92e9, 0xc7ce2dc16f159aa4, 0x45eafdc1f4d70cc0}}},
+{{{0x698401858045d72b, 0x4c22faa2cf2f0651, 0x941a36656b222dc6, 0x5a5eebc80362dade}},
+ {{0xb60e4624cfccb1ed, 0x59dbc292bd5c0395, 0x31a09d1ddc0481c9, 0x3f73ceea5d56d940}},
+ {{0xb7a7bfd10a4e8dc6, 0xbe57007e44c9b339, 0x60c1207f1557aefa, 0x26058891266218db}}},
+{{{0x59f704a68360ff04, 0xc3d93fde7661e6f4, 0x831b2a7312873551, 0x54ad0c2e4e615d57}},
+ {{0x4c818e3cc676e542, 0x5e422c9303ceccad, 0xec07cccab4129f08, 0x0dedfa10b24443b8}},
+ {{0xee3b67d5b82b522a, 0x36f163469fa5c1eb, 0xa5b4d2f26ec19fd3, 0x62ecb2baa77a9408}}},
+{{{0xe5ed795261152b3d, 0x4962357d0eddd7d1, 0x7482c8d0b96b4c71, 0x2e59f919a966d8be}},
+ {{0x92072836afb62874, 0x5fcd5e8579e104a5, 0x5aad01adc630a14a, 0x61913d5075663f98}},
+ {{0x0dc62d361a3231da, 0xfa47583294200270, 0x02d801513f9594ce, 0x3ddbc2a131c05d5c}}},
+{{{0x3f50a50a4ffb81ef, 0xb1e035093bf420bf, 0x9baa8e1cc6aa2cd0, 0x32239861fa237a40}},
+ {{0xfb735ac2004a35d1, 0x31de0f433a6607c3, 0x7b8591bfc528d599, 0x55be9a25f5bb050c}},
+ {{0x0d005acd33db3dbf, 0x0111b37c80ac35e2, 0x4892d66c6f88ebeb, 0x770eadb16508fbcd}}},
+{{{0x8451f9e05e4e89dd, 0xc06302ffbc793937, 0x5d22749556a6495c, 0x09a6755ca05603fb}},
+ {{0xf1d3b681a05071b9, 0x2207659a3592ff3a, 0x5f0169297881e40e, 0x16bedd0e86ba374e}},
+ {{0x5ecccc4f2c2737b5, 0x43b79e0c2dccb703, 0x33e008bc4ec43df3, 0x06c1b840f07566c0}}},
+{{{0x7688a5c6a388f877, 0x02a96c14deb2b6ac, 0x64c9f3431b8c2af8, 0x3628435554a1eed6}},
+ {{0x69ee9e7f9b02805c, 0xcbff828a547d1640, 0x3d93a869b2430968, 0x46b7b8cd3fe26972}},
+ {{0xe9812086fe7eebe0, 0x4cba6be72f515437, 0x1d04168b516efae9, 0x5ea1391043982cb9}}},
+{{{0x49125c9cf4702ee1, 0x4520b71f8b25b32d, 0x33193026501fef7e, 0x656d8997c8d2eb2b}},
+ {{0x6f2b3be4d5d3b002, 0xafec33d96a09c880, 0x035f73a4a8bcc4cc, 0x22c5b9284662198b}},
+ {{0xcb58c8fe433d8939, 0x89a0cb2e6a8d7e50, 0x79ca955309fbbe5a, 0x0c626616cd7fc106}}},
+{{{0x1ffeb80a4879b61f, 0x6396726e4ada21ed, 0x33c7b093368025ba, 0x471aa0c6f3c31788}},
+ {{0x8fdfc379fbf454b1, 0x45a5a970f1a4b771, 0xac921ef7bad35915, 0x42d088dca81c2192}},
+ {{0x8fda0f37a0165199, 0x0adadb77c8a0e343, 0x20fbfdfcc875e820, 0x1cf2bea80c2206e7}}},
+{{{0xc2ddf1deb36202ac, 0x92a5fe09d2e27aa5, 0x7d1648f6fc09f1d3, 0x74c2cc0513bc4959}},
+ {{0x982d6e1a02c0412f, 0x90fa4c83db58e8fe, 0x01c2f5bcdcb18bc0, 0x686e0c90216abc66}},
+ {{0x1fadbadba54395a7, 0xb41a02a0ae0da66a, 0xbf19f598bba37c07, 0x6a12b8acde48430d}}},
+{{{0xf8daea1f39d495d9, 0x592c190e525f1dfc, 0xdb8cbd04c9991d1b, 0x11f7fda3d88f0cb7}},
+ {{0x793bdd801aaeeb5f, 0x00a2a0aac1518871, 0xe8a373a31f2136b4, 0x48aab888fc91ef19}},
+ {{0x041f7e925830f40e, 0x002d6ca979661c06, 0x86dc9ff92b046a2e, 0x760360928b0493d1}}},
+{{{0x21bb41c6120cf9c6, 0xeab2aa12decda59b, 0xc1a72d020aa48b34, 0x215d4d27e87d3b68}},
+ {{0xb43108e5695a0b05, 0x6cb00ee8ad37a38b, 0x5edad6eea3537381, 0x3f2602d4b6dc3224}},
+ {{0xc8b247b65bcaf19c, 0x49779dc3b1b2c652, 0x89a180bbd5ece2e2, 0x13f098a3cec8e039}}},
+{{{0x9adc0ff9ce5ec54b, 0x039c2a6b8c2f130d, 0x028007c7f0f89515, 0x78968314ac04b36b}},
+ {{0xf3aa57a22796bb14, 0x883abab79b07da21, 0xe54be21831a0391c, 0x5ee7fb38d83205f9}},
+ {{0x538dfdcb41446a8e, 0xa5acfda9434937f9, 0x46af908d263c8c78, 0x61d0633c9bca0d09}}},
+{{{0x63744935ffdb2566, 0xc5bd6b89780b68bb, 0x6f1b3280553eec03, 0x6e965fd847aed7f5}},
+ {{0xada328bcf8fc73df, 0xee84695da6f037fc, 0x637fb4db38c2a909, 0x5b23ac2df8067bdc}},
+ {{0x9ad2b953ee80527b, 0xe88f19aafade6d8d, 0x0e711704150e82cf, 0x79b9bbb9dd95dedc}}},
+{{{0xebb355406a3126c2, 0xd26383a868c8c393, 0x6c0c6429e5b97a82, 0x5065f158c9fd2147}},
+ {{0xd1997dae8e9f7374, 0xa032a2f8cfbb0816, 0xcd6cba126d445f0a, 0x1ba811460accb834}},
+ {{0x708169fb0c429954, 0xe14600acd76ecf67, 0x2eaab98a70e645ba, 0x3981f39e58a4faf2}}},
+{{{0x18fb8a7559230a93, 0x1d168f6960e6f45d, 0x3a85a94514a93cb5, 0x38dc083705acd0fd}},
+ {{0xc845dfa56de66fde, 0xe152a5002c40483a, 0xe9d2e163c7b4f632, 0x30f4452edcbc1b65}},
+ {{0x856d2782c5759740, 0xfa134569f99cbecc, 0x8844fc73c0ea4e71, 0x632d9a1a593f2469}}},
+{{{0xf6bb6b15b807cba6, 0x1823c7dfbc54f0d7, 0xbb1d97036e29670b, 0x0b24f48847ed4a57}},
+ {{0xbf09fd11ed0c84a7, 0x63f071810d9f693a, 0x21908c2d57cf8779, 0x3a5a7df28af64ba2}},
+ {{0xdcdad4be511beac7, 0xa4538075ed26ccf2, 0xe19cff9f005f9a65, 0x34fcf74475481f63}}},
+{{{0xc197e04c789767ca, 0xb8714dcb38d9467d, 0x55de888283f95fa8, 0x3d3bdc164dfa63f7}},
+ {{0xa5bb1dab78cfaa98, 0x5ceda267190b72f2, 0x9309c9110a92608e, 0x0119a3042fb374b0}},
+ {{0x67a2d89ce8c2177d, 0x669da5f66895d0c1, 0xf56598e5b282a2b0, 0x56c088f1ede20a73}}},
+{{{0x336d3d1110a86e17, 0xd7f388320b75b2fa, 0xf915337625072988, 0x09674c6b99108b87}},
+ {{0x581b5fac24f38f02, 0xa90be9febae30cbd, 0x9a2169028acf92f0, 0x038b7ea48359038f}},
+ {{0x9f4ef82199316ff8, 0x2f49d282eaa78d4f, 0x0971a5ab5aef3174, 0x6e5e31025969eb65}}},
+{{{0xb16c62f587e593fb, 0x4999eddeca5d3e71, 0xb491c1e014cc3e6d, 0x08f5114789a8dba8}},
+ {{0x3304fb0e63066222, 0xfb35068987acba3f, 0xbd1924778c1061a3, 0x3058ad43d1838620}},
+ {{0x323c0ffde57663d0, 0x05c3df38a22ea610, 0xbdc78abdac994f9a, 0x26549fa4efe3dc99}}},
+{{{0x738b38d787ce8f89, 0xb62658e24179a88d, 0x30738c9cf151316d, 0x49128c7f727275c9}},
+ {{0x04dbbc17f75396b9, 0x69e6a2d7d2f86746, 0xc6409d99f53eabc6, 0x606175f6332e25d2}},
+ {{0x4021370ef540e7dd, 0x0910d6f5a1f1d0a5, 0x4634aacd5b06b807, 0x6a39e6356944f235}}},
+{{{0x96cd5640df90f3e7, 0x6c3a760edbfa25ea, 0x24f3ef0959e33cc4, 0x42889e7e530d2e58}},
+ {{0x1da1965774049e9d, 0xfbcd6ea198fe352b, 0xb1cbcd50cc5236a6, 0x1f5ec83d3f9846e2}},
+ {{0x8efb23c3328ccb75, 0xaf42a207dd876ee9, 0x20fbdadc5dfae796, 0x241e246b06bf9f51}}},
+{{{0x29e68e57ad6e98f6, 0x4c9260c80b462065, 0x3f00862ea51ebb4b, 0x5bc2c77fb38d9097}},
+ {{0x7eaafc9a6280bbb8, 0x22a70f12f403d809, 0x31ce40bb1bfc8d20, 0x2bc65635e8bd53ee}},
+ {{0xe8d5dc9fa96bad93, 0xe58fb17dde1947dc, 0x681532ea65185fa3, 0x1fdd6c3b034a7830}}},
+{{{0x0a64e28c55dc18fe, 0xe3df9e993399ebdd, 0x79ac432370e2e652, 0x35ff7fc33ae4cc0e}},
+ {{0x9c13a6a52dd8f7a9, 0x2dbb1f8c3efdcabf, 0x961e32405e08f7b5, 0x48c8a121bbe6c9e5}},
+ {{0xfc415a7c59646445, 0xd224b2d7c128b615, 0x6035c9c905fbb912, 0x42d7a91274429fab}}},
+{{{0x4e6213e3eaf72ed3, 0x6794981a43acd4e7, 0xff547cde6eb508cb, 0x6fed19dd10fcb532}},
+ {{0xa9a48947933da5bc, 0x4a58920ec2e979ec, 0x96d8800013e5ac4c, 0x453692d74b48b147}},
+ {{0xdd775d99a8559c6f, 0xf42a2140df003e24, 0x5223e229da928a66, 0x063f46ba6d38f22c}}},
+{{{0xd2d242895f536694, 0xca33a2c542939b2c, 0x986fada6c7ddb95c, 0x5a152c042f712d5d}},
+ {{0x39843cb737346921, 0xa747fb0738c89447, 0xcb8d8031a245307e, 0x67810f8e6d82f068}},
+ {{0x3eeb8fbcd2287db4, 0x72c7d3a301a03e93, 0x5473e88cbd98265a, 0x7324aa515921b403}}},
+{{{0x857942f46c3cbe8e, 0xa1d364b14730c046, 0x1c8ed914d23c41bf, 0x0838e161eef6d5d2}},
+ {{0xad23f6dae82354cb, 0x6962502ab6571a6d, 0x9b651636e38e37d1, 0x5cac5005d1a3312f}},
+ {{0x8cc154cce9e39904, 0x5b3a040b84de6846, 0xc4d8a61cb1be5d6e, 0x40fb897bd8861f02}}},
+{{{0x84c5aa9062de37a1, 0x421da5000d1d96e1, 0x788286306a9242d9, 0x3c5e464a690d10da}},
+ {{0xe57ed8475ab10761, 0x71435e206fd13746, 0x342f824ecd025632, 0x4b16281ea8791e7b}},
+ {{0xd1c101d50b813381, 0xdee60f1176ee6828, 0x0cb68893383f6409, 0x6183c565f6ff484a}}},
+{{{0x741d5a461e6bf9d6, 0x2305b3fc7777a581, 0xd45574a26474d3d9, 0x1926e1dc6401e0ff}},
+ {{0xdb468549af3f666e, 0xd77fcf04f14a0ea5, 0x3df23ff7a4ba0c47, 0x3a10dfe132ce3c85}},
+ {{0xe07f4e8aea17cea0, 0x2fd515463a1fc1fd, 0x175322fd31f2c0f1, 0x1fa1d01d861e5d15}}},
+{{{0xcc8055947d599832, 0x1e4656da37f15520, 0x99f6f7744e059320, 0x773563bc6a75cf33}},
+ {{0x38dcac00d1df94ab, 0x2e712bddd1080de9, 0x7f13e93efdd5e262, 0x73fced18ee9a01e5}},
+ {{0x06b1e90863139cb3, 0xa493da67c5a03ecd, 0x8d77cec8ad638932, 0x1f426b701b864f44}}},
+{{{0xefc9264c41911c01, 0xf1a3b7b817a22c25, 0x5875da6bf30f1447, 0x4e1af5271d31b090}},
+ {{0xf17e35c891a12552, 0xb76b8153575e9c76, 0xfa83406f0d9b723e, 0x0b76bb1b3fa7e438}},
+ {{0x08b8c1f97f92939b, 0xbe6771cbd444ab6e, 0x22e5646399bb8017, 0x7b6dd61eb772a955}}},
+{{{0xb7adc1e850f33d92, 0x7998fa4f608cd5cf, 0xad962dbd8dfc5bdb, 0x703e9bceaf1d2f4f}},
+ {{0x5730abf9ab01d2c7, 0x16fb76dc40143b18, 0x866cbe65a0cbb281, 0x53fa9b659bff6afe}},
+ {{0x6c14c8e994885455, 0x843a5d6665aed4e5, 0x181bb73ebcd65af1, 0x398d93e5c4c61f50}}},
+{{{0x1c4bd16733e248f3, 0xbd9e128715bf0a5f, 0xd43f8cf0a10b0376, 0x53b09b5ddf191b13}},
+ {{0xc3877c60d2e7e3f2, 0x3b34aaa030828bb1, 0x283e26e7739ef138, 0x699c9c9002c30577}},
+ {{0xf306a7235946f1cc, 0x921718b5cce5d97d, 0x28cdd24781b4e975, 0x51caf30c6fcdd907}}},
+{{{0xa60ba7427674e00a, 0x630e8570a17a7bf3, 0x3758563dcf3324cc, 0x5504aa292383fdaa}},
+ {{0x737af99a18ac54c7, 0x903378dcc51cb30f, 0x2b89bc334ce10cc7, 0x12ae29c189f8e99a}},
+ {{0xa99ec0cb1f0d01cf, 0x0dd1efcc3a34f7ae, 0x55ca7521d09c4e22, 0x5fd14fe958eba5ea}}},
+{{{0xb5dc2ddf2845ab2c, 0x069491b10a7fe993, 0x4daaf3d64002e346, 0x093ff26e586474d1}},
+ {{0x3c42fe5ebf93cb8e, 0xbedfa85136d4565f, 0xe0f0859e884220e8, 0x7dd73f960725d128}},
+ {{0xb10d24fe68059829, 0x75730672dbaf23e5, 0x1367253ab457ac29, 0x2f59bcbc86b470a4}}},
+{{{0x83847d429917135f, 0xad1b911f567d03d7, 0x7e7748d9be77aad1, 0x5458b42e2e51af4a}},
+ {{0x7041d560b691c301, 0x85201b3fadd7e71e, 0x16c2e16311335585, 0x2aa55e3d010828b1}},
+ {{0xed5192e60c07444f, 0x42c54e2d74421d10, 0x352b4c82fdb5c864, 0x13e9004a8a768664}}},
+{{{0xcbb5b5556c032bff, 0xdf7191b729297a3a, 0xc1ff7326aded81bb, 0x71ade8bb68be03f5}},
+ {{0x1e6284c5806b467c, 0xc5f6997be75d607b, 0x8b67d958b378d262, 0x3d88d66a81cd8b70}},
+ {{0x8b767a93204ed789, 0x762fcacb9fa0ae2a, 0x771febcc6dce4887, 0x343062158ff05fb3}}},
+{{{0xe05da1a7e1f5bf49, 0x26457d6dd4736092, 0x77dcb07773cc32f6, 0x0a5d94969cdd5fcd}},
+ {{0xfce219072a7b31b4, 0x4d7adc75aa578016, 0x0ec276a687479324, 0x6d6d9d5d1fda4beb}},
+ {{0x22b1a58ae9b08183, 0xfd95d071c15c388b, 0xa9812376850a0517, 0x33384cbabb7f335e}}},
+{{{0x3c6fa2680ca2c7b5, 0x1b5082046fb64fda, 0xeb53349c5431d6de, 0x5278b38f6b879c89}},
+ {{0x33bc627a26218b8d, 0xea80b21fc7a80c61, 0x9458b12b173e9ee6, 0x076247be0e2f3059}},
+ {{0x52e105f61416375a, 0xec97af3685abeba4, 0x26e6b50623a67c36, 0x5cf0e856f3d4fb01}}},
+{{{0xf6c968731ae8cab4, 0x5e20741ecb4f92c5, 0x2da53be58ccdbc3e, 0x2dddfea269970df7}},
+ {{0xbeaece313db342a8, 0xcba3635b842db7ee, 0xe88c6620817f13ef, 0x1b9438aa4e76d5c6}},
+ {{0x8a50777e166f031a, 0x067b39f10fb7a328, 0x1925c9a6010fbd76, 0x6df9b575cc740905}}},
+{{{0x42c1192927f6bdcf, 0x8f91917a403d61ca, 0xdc1c5a668b9e1f61, 0x1596047804ec0f8d}},
+ {{0xecdfc35b48cade41, 0x6a88471fb2328270, 0x740a4a2440a01b6a, 0x471e5796003b5f29}},
+ {{0xda96bbb3aced37ac, 0x7a2423b5e9208cea, 0x24cc5c3038aebae2, 0x50c356afdc5dae2f}}},
+{{{0x09dcbf4341c30318, 0xeeba061183181dce, 0xc179c0cedc1e29a1, 0x1dbf7b89073f35b0}},
+ {{0xcfed9cdf1b31b964, 0xf486a9858ca51af3, 0x14897265ea8c1f84, 0x784a53dd932acc00}},
+ {{0x2d99f9df14fc4920, 0x76ccb60cc4499fe5, 0xa4132cbbe5cf0003, 0x3f93d82354f000ea}}},
+{{{0x8183e7689e04ce85, 0x678fb71e04465341, 0xad92058f6688edac, 0x5da350d3532b099a}},
+ {{0xeaac12d179e14978, 0xff923ff3bbebff5e, 0x4af663e40663ce27, 0x0fd381a811a5f5ff}},
+ {{0xf256aceca436df54, 0x108b6168ae69d6e8, 0x20d986cb6b5d036c, 0x655957b9fee2af50}}},
+{{{0xaea8b07fa902030f, 0xf88c766af463d143, 0x15b083663c787a60, 0x08eab1148267a4a8}},
+ {{0xbdc1409bd002d0ac, 0x66660245b5ccd9a6, 0x82317dc4fade85ec, 0x02fe934b6ad7df0d}},
+ {{0xef5cf100cfb7ea74, 0x22897633a1cb42ac, 0xd4ce0c54cef285e2, 0x30408c048a146a55}}},
+{{{0x739d8845832fcedb, 0xfa38d6c9ae6bf863, 0x32bc0dcab74ffef7, 0x73937e8814bce45e}},
+ {{0xbb2e00c9193b877f, 0xece3a890e0dc506b, 0xecf3b7c036de649f, 0x5f46040898de9e1a}},
+ {{0xb9037116297bf48d, 0xa9d13b22d4f06834, 0xe19715574696bdc6, 0x2cf8a4e891d5e835}}},
+{{{0x6d93fd8707110f67, 0xdd4c09d37c38b549, 0x7cb16a4cc2736a86, 0x2049bd6e58252a09}},
+ {{0x2cb5487e17d06ba2, 0x24d2381c3950196b, 0xd7659c8185978a30, 0x7a6f7f2891d6a4f6}},
+ {{0x7d09fd8d6a9aef49, 0xf0ee60be5b3db90b, 0x4c21b52c519ebfd4, 0x6011aadfc545941d}}},
+{{{0x5f67926dcf95f83c, 0x7c7e856171289071, 0xd6a1e7f3998f7a5b, 0x6fc5cc1b0b62f9e0}},
+ {{0x63ded0c802cbf890, 0xfbd098ca0dff6aaa, 0x624d0afdb9b6ed99, 0x69ce18b779340b1e}},
+ {{0xd1ef5528b29879cb, 0xdd1aae3cd47e9092, 0x127e0442189f2352, 0x15596b3ae57101f1}}},
+{{{0x462739d23f9179a2, 0xff83123197d6ddcf, 0x1307deb553f2148a, 0x0d2237687b5f4dda}},
+ {{0x09ff31167e5124ca, 0x0be4158bd9c745df, 0x292b7d227ef556e5, 0x3aa4e241afb6d138}},
+ {{0x2cc138bf2a3305f5, 0x48583f8fa2e926c3, 0x083ab1a25549d2eb, 0x32fcaa6e4687a36c}}},
+{{{0x7bc56e8dc57d9af5, 0x3e0bd2ed9df0bdf2, 0xaac014de22efe4a3, 0x4627e9cefebd6a5c}},
+ {{0x3207a4732787ccdf, 0x17e31908f213e3f8, 0xd5b2ecd7f60d964e, 0x746f6336c2600be9}},
+ {{0x3f4af345ab6c971c, 0xe288eb729943731f, 0x33596a8a0344186d, 0x7b4917007ed66293}}},
+{{{0x2d85fb5cab84b064, 0x497810d289f3bc14, 0x476adc447b15ce0c, 0x122ba376f844fd7b}},
+ {{0x54341b28dd53a2dd, 0xaa17905bdf42fc3f, 0x0ff592d94dd2f8f4, 0x1d03620fe08cd37d}},
+ {{0xc20232cda2b4e554, 0x9ed0fd42115d187f, 0x2eabb4be7dd479d9, 0x02c70bf52b68ec4c}}},
+{{{0xa287ec4b5d0b2fbb, 0x415c5790074882ca, 0xe044a61ec1d0815c, 0x26334f0a409ef5e0}},
+ {{0xace532bf458d72e1, 0x5be768e07cb73cb5, 0x56cf7d94ee8bbde7, 0x6b0697e3feb43a03}},
+ {{0xb6c8f04adf62a3c0, 0x3ef000ef076da45d, 0x9c9cb95849f0d2a9, 0x1cc37f43441b2fae}}},
+{{{0x508f565a5cc7324f, 0xd061c4c0e506a922, 0xfb18abdb5c45ac19, 0x6c6809c10380314a}},
+ {{0xd76656f1c9ceaeb9, 0x1c5b15f818e5656a, 0x26e72832844c2334, 0x3a346f772f196838}},
+ {{0xd2d55112e2da6ac8, 0xe9bd0331b1e851ed, 0x960746dd8ec67262, 0x05911b9f6ef7c5d0}}},
+{{{0xe9dcd756b637ff2d, 0xec4c348fc987f0c4, 0xced59285f3fbc7b7, 0x3305354793e1ea87}},
+ {{0x01c18980c5fe9f94, 0xcd656769716fd5c8, 0x816045c3d195a086, 0x6e2b7f3266cc7982}},
+ {{0xcc802468f7c3568f, 0x9de9ba8219974cb3, 0xabb7229cb5b81360, 0x44e2017a6fbeba62}}},
+{{{0xc4c2a74354dab774, 0x8e5d4c3c4eaf031a, 0xb76c23d242838f17, 0x749a098f68dce4ea}},
+ {{0x87f82cf3b6ca6ecd, 0x580f893e18f4a0c2, 0x058930072604e557, 0x6cab6ac256d19c1d}},
+ {{0xdcdfe0a02cc1de60, 0x032665ff51c5575b, 0x2c0c32f1073abeeb, 0x6a882014cd7b8606}}},
+{{{0xa52a92fea4747fb5, 0xdc12a4491fa5ab89, 0xd82da94bb847a4ce, 0x4d77edce9512cc4e}},
+ {{0xd111d17caf4feb6e, 0x050bba42b33aa4a3, 0x17514c3ceeb46c30, 0x54bedb8b1bc27d75}},
+ {{0x77c8e14577e2189c, 0xa3e46f6aff99c445, 0x3144dfc86d335343, 0x3a96559e7c4216a9}}},
+{{{0x12550d37f42ad2ee, 0x8b78e00498a1fbf5, 0x5d53078233894cb2, 0x02c84e4e3e498d0c}},
+ {{0x4493896880baaa52, 0x4c98afc4f285940e, 0xef4aa79ba45448b6, 0x5278c510a57aae7f}},
+ {{0xa54dd074294c0b94, 0xf55d46b8df18ffb6, 0xf06fecc58dae8366, 0x588657668190d165}}},
+{{{0xd47712311aef7117, 0x50343101229e92c7, 0x7a95e1849d159b97, 0x2449959b8b5d29c9}},
+ {{0xbf5834f03de25cc3, 0xb887c8aed6815496, 0x5105221a9481e892, 0x6760ed19f7723f93}},
+ {{0x669ba3b7ac35e160, 0x2eccf73fba842056, 0x1aec1f17c0804f07, 0x0d96bc031856f4e7}}},
+{{{0x3318be7775c52d82, 0x4cb764b554d0aab9, 0xabcf3d27cc773d91, 0x3bf4d1848123288a}},
+ {{0xb1d534b0cc7505e1, 0x32cd003416c35288, 0xcb36a5800762c29d, 0x5bfe69b9237a0bf8}},
+ {{0x183eab7e78a151ab, 0xbbe990c999093763, 0xff717d6e4ac7e335, 0x4c5cddb325f39f88}}},
+{{{0xc0f6b74d6190a6eb, 0x20ea81a42db8f4e4, 0xa8bd6f7d97315760, 0x33b1d60262ac7c21}},
+ {{0x57750967e7a9f902, 0x2c37fdfc4f5b467e, 0xb261663a3177ba46, 0x3a375e78dc2d532b}},
+ {{0x8141e72f2d4dddea, 0xe6eafe9862c607c8, 0x23c28458573cafd0, 0x46b9476f4ff97346}}},
+{{{0x0c1ffea44f901e5c, 0x2b0b6fb72184b782, 0xe587ff910114db88, 0x37130f364785a142}},
+ {{0x1215505c0d58359f, 0x2a2013c7fc28c46b, 0x24a0a1af89ea664e, 0x4400b638a1130e1f}},
+ {{0x3a01b76496ed19c3, 0x31e00ab0ed327230, 0x520a885783ca15b1, 0x06aab9875accbec7}}},
+{{{0xc1339983f5df0ebb, 0xc0f3758f512c4cac, 0x2cf1130a0bb398e1, 0x6b3cecf9aa270c62}},
+ {{0x5349acf3512eeaef, 0x20c141d31cc1cb49, 0x24180c07a99a688d, 0x555ef9d1c64b2d17}},
+ {{0x36a770ba3b73bd08, 0x624aef08a3afbf0c, 0x5737ff98b40946f2, 0x675f4de13381749d}}},
+{{{0x0e2c52036b1782fc, 0x64816c816cad83b4, 0xd0dcbdd96964073e, 0x13d99df70164c520}},
+ {{0xa12ff6d93bdab31d, 0x0725d80f9d652dfe, 0x019c4ff39abe9487, 0x60f450b882cd3c43}},
+ {{0x014b5ec321e5c0ca, 0x4fcb69c9d719bfa2, 0x4e5f1c18750023a0, 0x1c06de9e55edac80}}},
+{{{0x990f7ad6a33ec4e2, 0x6608f938be2ee08e, 0x9ca143c563284515, 0x4cf38a1fec2db60d}},
+ {{0xffd52b40ff6d69aa, 0x34530b18dc4049bb, 0x5e4a5c2fa34d9897, 0x78096f8e7d32ba2d}},
+ {{0xa0aaaa650dfa5ce7, 0xf9c49e2a48b5478c, 0x4f09cc7d7003725b, 0x373cad3a26091abe}}},
+{{{0xb294634d82c9f57c, 0x1fcbfde124934536, 0x9e9c4db3418cdb5a, 0x0040f3d9454419fc}},
+ {{0xf1bea8fb89ddbbad, 0x3bcb2cbc61aeaecb, 0x8f58a7bb1f9b8d9d, 0x21547eda5112a686}},
+ {{0xdefde939fd5986d3, 0xf4272c89510a380c, 0xb72ba407bb3119b9, 0x63550a334a254df4}}},
+{{{0x6507d6edb569cf37, 0x178429b00ca52ee1, 0xea7c0090eb6bd65d, 0x3eea62c7daf78f51}},
+ {{0x9bba584572547b49, 0xf305c6fae2c408e0, 0x60e8fa69c734f18d, 0x39a92bafaa7d767a}},
+ {{0x9d24c713e693274e, 0x5f63857768dbd375, 0x70525560eb8ab39a, 0x68436a0665c9c4cd}}},
+{{{0xbc0235e8202f3f27, 0xc75c00e264f975b0, 0x91a4e9d5a38c2416, 0x17b6e7f68ab789f9}},
+ {{0x1e56d317e820107c, 0xc5266844840ae965, 0xc1e0a1c6320ffc7a, 0x5373669c91611472}},
+ {{0x5d2814ab9a0e5257, 0x908f2084c9cab3fc, 0xafcaf5885b2d1eca, 0x1cb4b5a678f87d11}}},
+{{{0xb664c06b394afc6c, 0x0c88de2498da5fb1, 0x4f8d03164bcad834, 0x330bca78de7434a2}},
+ {{0x6b74aa62a2a007e7, 0xf311e0b0f071c7b1, 0x5707e438000be223, 0x2dc0fd2d82ef6eac}},
+ {{0x982eff841119744e, 0xf9695e962b074724, 0xc58ac14fbfc953fb, 0x3c31be1b369f1cf5}}},
+{{{0xb0f4864d08948aee, 0x07dc19ee91ba1c6f, 0x7975cdaea6aca158, 0x330b61134262d4bb}},
+ {{0xc168bc93f9cb4272, 0xaeb8711fc7cedb98, 0x7f0e52aa34ac8d7a, 0x41cec1097e7d55bb}},
+ {{0xf79619d7a26d808a, 0xbb1fd49e1d9e156d, 0x73d7c36cdba1df27, 0x26b44cd91f28777d}}},
+{{{0x300a9035393aa6d8, 0x2b501131a12bb1cd, 0x7b1ff677f093c222, 0x4309c1f8cab82bad}},
+ {{0xaf44842db0285f37, 0x8753189047efc8df, 0x9574e091f820979a, 0x0e378d6069615579}},
+ {{0xd9fa917183075a55, 0x4bdb5ad26b009fdc, 0x7829ad2cd63def0e, 0x078fc54975fd3877}}},
+{{{0x87dfbd1428878f2d, 0x134636dd1e9421a1, 0x4f17c951257341a3, 0x5df98d4bad296cb8}},
+ {{0xe2004b5bb833a98a, 0x44775dec2d4c3330, 0x3aa244067eace913, 0x272630e3d58e00a9}},
+ {{0xf3678fd0ecc90b54, 0xf001459b12043599, 0x26725fbc3758b89b, 0x4325e4aa73a719ae}}},
+{{{0x657dc6ef433c3493, 0x65375e9f80dbf8c3, 0x47fd2d465b372dae, 0x4966ab79796e7947}},
+ {{0xed24629acf69f59d, 0x2a4a1ccedd5abbf4, 0x3535ca1f56b2d67b, 0x5d8c68d043b1b42d}},
+ {{0xee332d4de3b42b0a, 0xd84e5a2b16a4601c, 0x78243877078ba3e4, 0x77ed1eb4184ee437}}},
+{{{0xbfd4e13f201839a0, 0xaeefffe23e3df161, 0xb65b04f06b5d1fe3, 0x52e085fb2b62fbc0}},
+ {{0x185d43f89e92ed1a, 0xb04a1eeafe4719c6, 0x499fbe88a6f03f4f, 0x5d8b0d2f3c859bdd}},
+ {{0x124079eaa54cf2ba, 0xd72465eb001b26e7, 0x6843bcfdc97af7fd, 0x0524b42b55eacd02}}},
+{{{0xfd0d5dbee45447b0, 0x6cec351a092005ee, 0x99a47844567579cb, 0x59d242a216e7fa45}},
+ {{0xbc18dcad9b829eac, 0x23ae7d28b5f579d0, 0xc346122a69384233, 0x1a6110b2e7d4ac89}},
+ {{0x4f833f6ae66997ac, 0x6849762a361839a4, 0x6985dec1970ab525, 0x53045e89dcb1f546}}},
+{{{0xcb8bb346d75353db, 0xfcfcb24bae511e22, 0xcba48d40d50ae6ef, 0x26e3bae5f4f7cb5d}},
+ {{0x84da3cde8d45fe12, 0xbd42c218e444e2d2, 0xa85196781f7e3598, 0x7642c93f5616e2b2}},
+ {{0x2323daa74595f8e4, 0xde688c8b857abeb4, 0x3fc48e961c59326e, 0x0b2e73ca15c9b8ba}}},
+{{{0xd6bb4428c17f5026, 0x9eb27223fb5a9ca7, 0xe37ba5031919c644, 0x21ce380db59a6602}},
+ {{0x0e3fbfaf79c03a55, 0x3077af054cbb5acf, 0xd5c55245db3de39f, 0x015e68c1476a4af7}},
+ {{0xc1d5285220066a38, 0x95603e523570aef3, 0x832659a7226b8a4d, 0x5dd689091f8eedc9}}},
+{{{0xcbac84debfd3c856, 0x1624c348b35ff244, 0xb7f88dca5d9cad07, 0x3b0e574da2c2ebe8}},
+ {{0x1d022591a5313084, 0xca2d4aaed6270872, 0x86a12b852f0bfd20, 0x56e6c439ad7da748}},
+ {{0xc704ff4942bdbae6, 0x5e21ade2b2de1f79, 0xe95db3f35652fad8, 0x0822b5378f08ebc1}}},
+{{{0x51f048478f387475, 0xb25dbcf49cbecb3c, 0x9aab1244d99f2055, 0x2c709e6c1c10a5d6}},
+ {{0xe1b7f29362730383, 0x4b5279ffebca8a2c, 0xdafc778abfd41314, 0x7deb10149c72610f}},
+ {{0xcb62af6a8766ee7a, 0x66cbec045553cd0e, 0x588001380f0be4b5, 0x08e68e9ff62ce2ea}}},
+{{{0x34ad500a4bc130ad, 0x8d38db493d0bd49c, 0xa25c3d98500a89be, 0x2f1f3f87eeba3b09}},
+ {{0x2f2d09d50ab8f2f9, 0xacb9218dc55923df, 0x4a8f342673766cb9, 0x4cb13bd738f719f5}},
+ {{0xf7848c75e515b64a, 0xa59501badb4a9038, 0xc20d313f3f751b50, 0x19a1e353c0ae2ee8}}},
+{{{0x7d1c7560bafa05c3, 0xb3e1a0a0c6e55e61, 0xe3529718c0d66473, 0x41546b11c20c3486}},
+ {{0xb42172cdd596bdbd, 0x93e0454398eefc40, 0x9fb15347b44109b5, 0x736bd3990266ae34}},
+ {{0x85532d509334b3b4, 0x46fd114b60816573, 0xcc5f5f30425c8375, 0x412295a2b87fab5c}}},
+{{{0x19c99b88f57ed6e9, 0x5393cb266df8c825, 0x5cee3213b30ad273, 0x14e153ebb52d2e34}},
+ {{0x2e655261e293eac6, 0x845a92032133acdb, 0x460975cb7900996b, 0x0760bb8d195add80}},
+ {{0x413e1a17cde6818a, 0x57156da9ed69a084, 0x2cbf268f46caccb1, 0x6b34be9bc33ac5f2}}},
+{{{0xf3df2f643a78c0b2, 0x4c3e971ef22e027c, 0xec7d1c5e49c1b5a3, 0x2012c18f0922dd2d}},
+ {{0x11fc69656571f2d3, 0xc6c9e845530e737a, 0xe33ae7a2d4fe5035, 0x01b9c7b62e6dd30b}},
+ {{0x880b55e55ac89d29, 0x1483241f45a0a763, 0x3d36efdfc2e76c1f, 0x08af5b784e4bade8}}},
+{{{0x283499dc881f2533, 0x9d0525da779323b6, 0x897addfb673441f4, 0x32b79d71163a168d}},
+ {{0xe27314d289cc2c4b, 0x4be4bd11a287178d, 0x18d528d6fa3364ce, 0x6423c1d5afd9826e}},
+ {{0xcc85f8d9edfcb36a, 0x22bcc28f3746e5f9, 0xe49de338f9e5d3cd, 0x480a5efbc13e2dcc}}},
+{{{0x0b51e70b01622071, 0x06b505cf8b1dafc5, 0x2c6bb061ef5aabcd, 0x47aa27600cb7bf31}},
+ {{0xb6614ce442ce221f, 0x6e199dcc4c053928, 0x663fb4a4dc1cbe03, 0x24b31d47691c8e06}},
+ {{0x2a541eedc015f8c3, 0x11a4fe7e7c693f7c, 0xf0af66134ea278d6, 0x545b585d14dda094}}},
+{{{0x67bf275ea0d43a0f, 0xade68e34089beebe, 0x4289134cd479e72e, 0x0f62f9c332ba5454}},
+ {{0x6204e4d0e3b321e1, 0x3baa637a28ff1e95, 0x0b0ccffd5b99bd9e, 0x4d22dc3e64c8d071}},
+ {{0xfcb46589d63b5f39, 0x5cae6a3f57cbcf61, 0xfebac2d2953afa05, 0x1c0fa01a36371436}}},
+{{{0xe7547449bc7cd692, 0x0f9abeaae6f73ddf, 0x4af01ca700837e29, 0x63ab1b5d3f1bc183}},
+ {{0xc11ee5e854c53fae, 0x6a0b06c12b4f3ff4, 0x33540f80e0b67a72, 0x15f18fc3cd07e3ef}},
+ {{0x32750763b028f48c, 0x06020740556a065f, 0xd53bd812c3495b58, 0x08706c9b865f508d}}},
+{{{0xf37ca2ab3d343dff, 0x1a8c6a2d80abc617, 0x8e49e035d4ccffca, 0x48b46beebaa1d1b9}},
+ {{0xcc991b4138b41246, 0x243b9c526f9ac26b, 0xb9ef494db7cbabbd, 0x5fba433dd082ed00}},
+ {{0x9c49e355c9941ad0, 0xb9734ade74498f84, 0x41c3fed066663e5c, 0x0ecfedf8e8e710b3}}},
+{{{0x76430f9f9cd470d9, 0xb62acc9ba42f6008, 0x1898297c59adad5e, 0x7789dd2db78c5080}},
+ {{0x744f7463e9403762, 0xf79a8dee8dfcc9c9, 0x163a649655e4cde3, 0x3b61788db284f435}},
+ {{0xb22228190d6ef6b2, 0xa94a66b246ce4bfa, 0x46c1a77a4f0b6cc7, 0x4236ccffeb7338cf}}},
+{{{0x8497404d0d55e274, 0x6c6663d9c4ad2b53, 0xec2fb0d9ada95734, 0x2617e120cdb8f73c}},
+ {{0x3bd82dbfda777df6, 0x71b177cc0b98369e, 0x1d0e8463850c3699, 0x5a71945b48e2d1f1}},
+ {{0x6f203dd5405b4b42, 0x327ec60410b24509, 0x9c347230ac2a8846, 0x77de29fc11ffeb6a}}},
+{{{0xb0ac57c983b778a8, 0x53cdcca9d7fe912c, 0x61c2b854ff1f59dc, 0x3a1a2cf0f0de7dac}},
+ {{0x835e138fecced2ca, 0x8c9eaf13ea963b9a, 0xc95fbfc0b2160ea6, 0x575e66f3ad877892}},
+ {{0x99803a27c88fcb3a, 0x345a6789275ec0b0, 0x459789d0ff6c2be5, 0x62f882651e70a8b2}}},
+{{{0x085ae2c759ff1be4, 0x149145c93b0e40b7, 0xc467e7fa7ff27379, 0x4eeecf0ad5c73a95}},
+ {{0x6d822986698a19e0, 0xdc9821e174d78a71, 0x41a85f31f6cb1f47, 0x352721c2bcda9c51}},
+ {{0x48329952213fc985, 0x1087cf0d368a1746, 0x8e5261b166c15aa5, 0x2d5b2d842ed24c21}}},
+{{{0x02cfebd9ebd3ded1, 0xd45b217739021974, 0x7576f813fe30a1b7, 0x5691b6f9a34ef6c2}},
+ {{0x5eb7d13d196ac533, 0x377234ecdb80be2b, 0xe144cffc7cf5ae24, 0x5226bcf9c441acec}},
+ {{0x79ee6c7223e5b547, 0x6f5f50768330d679, 0xed73e1e96d8adce9, 0x27c3da1e1d8ccc03}}},
+{{{0x7eb9efb23fe24c74, 0x3e50f49f1651be01, 0x3ea732dc21858dea, 0x17377bd75bb810f9}},
+ {{0x28302e71630ef9f6, 0xc2d4a2032b64cee0, 0x090820304b6292be, 0x5fca747aa82adf18}},
+ {{0x232a03c35c258ea5, 0x86f23a2c6bcb0cf1, 0x3dad8d0d2e442166, 0x04a8933cab76862b}}},
+{{{0xd2c604b622943dff, 0xbc8cbece44cfb3a0, 0x5d254ff397808678, 0x0fa3614f3b1ca6bf}},
+ {{0x69082b0e8c936a50, 0xf9c9a035c1dac5b6, 0x6fb73e54c4dfb634, 0x4005419b1d2bc140}},
+ {{0xa003febdb9be82f0, 0x2089c1af3a44ac90, 0xf8499f911954fa8e, 0x1fba218aef40ab42}}},
+{{{0xab549448fac8f53e, 0x81f6e89a7ba63741, 0x74fd6c7d6c2b5e01, 0x392e3acaa8c86e42}},
+ {{0x4f3e57043e7b0194, 0xa81d3eee08daaf7f, 0xc839c6ab99dcdef1, 0x6c535d13ff7761d5}},
+ {{0x4cbd34e93e8a35af, 0x2e0781445887e816, 0x19319c76f29ab0ab, 0x25e17fe4d50ac13b}}},
+{{{0x0a289bd71e04f676, 0x208e1c52d6420f95, 0x5186d8b034691fab, 0x255751442a9fb351}},
+ {{0x915f7ff576f121a7, 0xc34a32272fcd87e3, 0xccba2fde4d1be526, 0x6bba828f8969899b}},
+ {{0xe2d1bc6690fe3901, 0x4cb54a18a0997ad5, 0x971d6914af8460d4, 0x559d504f7f6b7be4}}},
+{{{0xa7738378b3eb54d5, 0x1d69d366a5553c7c, 0x0a26cf62f92800ba, 0x01ab12d5807e3217}},
+ {{0x9c4891e7f6d266fd, 0x0744a19b0307781b, 0x88388f1d6061e23b, 0x123ea6a3354bd50e}},
+ {{0x118d189041e32d96, 0xb9ede3c2d8315848, 0x1eab4271d83245d9, 0x4a3961e2c918a154}}},
+{{{0x71dc3be0f8e6bba0, 0xd6cef8347effe30a, 0xa992425fe13a476a, 0x2cd6bce3fb1db763}},
+ {{0x0327d644f3233f1e, 0x499a260e34fcf016, 0x83b5a716f2dab979, 0x68aceead9bd4111f}},
+ {{0x38b4c90ef3d7c210, 0x308e6e24b7ad040c, 0x3860d9f1b7e73e23, 0x595760d5b508f597}}},
+{{{0x6129bfe104aa6397, 0x8f960008a4a7fccb, 0x3f8bc0897d909458, 0x709fa43edcb291a9}},
+ {{0x882acbebfd022790, 0x89af3305c4115760, 0x65f492e37d3473f4, 0x2cb2c5df54515a2b}},
+ {{0xeb0a5d8c63fd2aca, 0xd22bc1662e694eff, 0x2723f36ef8cbb03a, 0x70f029ecf0c8131f}}},
+{{{0x461307b32eed3e33, 0xae042f33a45581e7, 0xc94449d3195f0366, 0x0b7d5d8a6c314858}},
+ {{0x2a6aafaa5e10b0b9, 0x78f0a370ef041aa9, 0x773efb77aa3ad61f, 0x44eca5a2a74bd9e1}},
+ {{0x25d448327b95d543, 0x70d38300a3340f1d, 0xde1c531c60e1c52b, 0x272224512c7de9e4}}},
+{{{0x1abc92af49c5342e, 0xffeed811b2e6fad0, 0xefa28c8dfcc84e29, 0x11b5df18a44cc543}},
+ {{0xbf7bbb8a42a975fc, 0x8c5c397796ada358, 0xe27fc76fcdedaa48, 0x19735fd7f6bc20a6}},
+ {{0xe3ab90d042c84266, 0xeb848e0f7f19547e, 0x2503a1d065a497b9, 0x0fef911191df895f}}},
+{{{0xb1507ca1ab1c6eb9, 0xbd448f3e16b687b3, 0x3455fb7f2c7a91ab, 0x7579229e2f2adec1}},
+ {{0x6ab5dcb85b1c16b7, 0x94c0fce83c7b27a5, 0xa4b11c1a735517be, 0x499238d0ba0eafaa}},
+ {{0xecf46e527aba8b57, 0x15a08c478bd1647b, 0x7af1c6a65f706fef, 0x6345fa78f03a30d5}}},
+{{{0xdf02f95f1015e7a1, 0x790ec41da9b40263, 0x4d3a0ea133ea1107, 0x54f70be7e33af8c9}},
+ {{0x93d3cbe9bdd8f0a4, 0xdb152c1bfd177302, 0x7dbddc6d7f17a875, 0x3e1a71cc8f426efe}},
+ {{0xc83ca3e390babd62, 0x80ede3670291c833, 0xc88038ccd37900c4, 0x2c5fc0231ec31fa1}}},
+{{{0xfeba911717038b4f, 0xe5123721c9deef81, 0x1c97e4e75d0d8834, 0x68afae7a23dc3bc6}},
+ {{0xc422e4d102456e65, 0x87414ac1cad47b91, 0x1592e2bba2b6ffdd, 0x75d9d2bff5c2100f}},
+ {{0x5bd9b4763626e81c, 0x89966936bca02edd, 0x0a41193d61f077b3, 0x3097a24200ce5471}}},
+{{{0x57427734c7f8b84c, 0xf141a13e01b270e9, 0x02d1adfeb4e564a6, 0x4bb23d92ce83bd48}},
+ {{0xa162e7246695c486, 0x131d633435a89607, 0x30521561a0d12a37, 0x56704bada6afb363}},
+ {{0xaf6c4aa752f912b9, 0x5e665f6cd86770c8, 0x4c35ac83a3c8cd58, 0x2b7a29c010a58a7e}}},
+{{{0xc4007f77d0c1cec3, 0x8d1020b6bac492f8, 0x32ec29d57e69daaf, 0x599408759d95fce0}},
+ {{0x33810a23bf00086e, 0xafce925ee736ff7c, 0x3d60e670e24922d4, 0x11ce9e714f96061b}},
+ {{0x219ef713d815bac1, 0xf141465d485be25c, 0x6d5447cc4e513c51, 0x174926be5ef44393}}},
+{{{0xb5deb2f9fc5bd5bb, 0x92daa72ae1d810e1, 0xafc4cfdcb72a1c59, 0x497d78813fc22a24}},
+ {{0x3ef5d41593ea022e, 0x5cbcc1a20ed0eed6, 0x8fd24ecf07382c8c, 0x6fa42ead06d8e1ad}},
+ {{0xe276824a1f73371f, 0x7f7cf01c4f5b6736, 0x7e201fe304fa46e7, 0x785a36a357808c96}}},
+{{{0x825fbdfd63014d2b, 0xc852369c6ca7578b, 0x5b2fcd285c0b5df0, 0x12ab214c58048c8f}},
+ {{0x070442985d517bc3, 0x6acd56c7ae653678, 0x00a27983985a7763, 0x5167effae512662b}},
+ {{0xbd4ea9e10f53c4b6, 0x1673dc5f8ac91a14, 0xa8f81a4e2acc1aba, 0x33a92a7924332a25}}},
+{{{0x9dd1f49927996c02, 0x0cb3b058e04d1752, 0x1f7e88967fd02c3e, 0x2f964268cb8b3eb1}},
+ {{0x7ba95ba0218f2ada, 0xcff42287330fb9ca, 0xdada496d56c6d907, 0x5380c296f4beee54}},
+ {{0x9d4f270466898d0a, 0x3d0987990aff3f7a, 0xd09ef36267daba45, 0x7761455e7b1c669c}}} \ No newline at end of file
diff --git a/ext/ed25519-amd64-asm/ge25519_base_niels_smalltables.data b/ext/ed25519-amd64-asm/ge25519_base_niels_smalltables.data
new file mode 100644
index 00000000..a31f6f2f
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_base_niels_smalltables.data
@@ -0,0 +1,768 @@
+{{{0x9d103905d740913e, 0xfd399f05d140beb3, 0xa5c18434688f8a09, 0x44fd2f9298f81267}},
+ {{0x2fbc93c6f58c3b85, 0xcf932dc6fb8c0e19, 0x270b4898643d42c2, 0x07cf9d3a33d4ba65}},
+ {{0xdbbd15674b6fbb59, 0x41e13f00eea2a5ea, 0xcdd49d1cc957c6fa, 0x4f0ebe1faf16ecca}}},
+{{{0x8a99a56042b4d5a8, 0x8f2b810c4e60acf6, 0xe09e236bb16e37aa, 0x6bb595a669c92555}},
+ {{0x9224e7fc933c71d7, 0x9f469d967a0ff5b5, 0x5aa69a65e1d60702, 0x590c063fa87d2e2e}},
+ {{0x6e347eaadad36802, 0xbaf3599383ee4805, 0x3bcabe10e6076826, 0x49314f0a165ed1b8}}},
+{{{0x56611fe8a4fcd265, 0x3bd353fde5c1ba7d, 0x8131f31a214bd6bd, 0x2ab91587555bda62}},
+ {{0xaf25b0a84cee9730, 0x025a8430e8864b8a, 0xc11b50029f016732, 0x7a164e1b9a80f8f4}},
+ {{0x9bf211f4f1674834, 0xb84e6b17f62df895, 0xd7de6f075b722a4e, 0x549a04b963bb2a21}}},
+{{{0x95fe050a056818bf, 0x327e89715660faa9, 0xc3e8e3cd06a05073, 0x27933f4c7445a49a}},
+ {{0x287351b98efc099f, 0x6765c6f47dfd2538, 0xca348d3dfb0a9265, 0x680e910321e58727}},
+ {{0xbf1e45ece51426b0, 0xe32bc63d6dba0f94, 0xe42974d58cf852c0, 0x44f079b1b0e64c18}}},
+{{{0x7f9182c3a447d6ba, 0xd50014d14b2729b7, 0xe33cf11cb864a087, 0x154a7e73eb1b55f3}},
+ {{0xa212bc4408a5bb33, 0x8d5048c3c75eed02, 0xdd1beb0c5abfec44, 0x2945ccf146e206eb}},
+ {{0xc832a179e7d003b3, 0x5f729d0a00124d7e, 0x62c1d4a10e6d8ff3, 0x68b8ac5938b27a98}}},
+{{{0x499806b67b7d8ca4, 0x575be28427d22739, 0xbb085ce7204553b9, 0x38b64c41ae417884}},
+ {{0x3a0ceeeb77157131, 0x9b27158900c8af88, 0x8065b668da59a736, 0x51e57bb6a2cc38bd}},
+ {{0x8f9dad91689de3a4, 0x175f2428f8fb9137, 0x050ab5329fcfb988, 0x7865dfa21354c09f}}},
+{{{0xba6f2c9aaa3221b1, 0x6ca021533bba23a7, 0x9dea764f92192c3a, 0x1d6edd5d2e5317e0}},
+ {{0x6b1a5cd0944ea3bf, 0x7470353ab39dc0d2, 0x71b2528228542e49, 0x461bea69283c927e}},
+ {{0x217a8aacab0fda36, 0xa528c6543d3549c8, 0x37d05b8b13ab7568, 0x233cef623a2cbc37}}},
+{{{0xe2a75dedf39234d9, 0x963d7680e1b558f9, 0x2c2741ac6e3c23fb, 0x3a9024a1320e01c3}},
+ {{0x59b7596604dd3e8f, 0x6cb30377e288702c, 0xb1339c665ed9c323, 0x0915e76061bce52f}},
+ {{0xdf7de835a834a37e, 0x8be19cda689857ea, 0x2c1185367167b326, 0x589eb3d9dbefd5c2}}},
+{{{0xed5b635449aa515e, 0xa865c49f0bc6823a, 0x850c1fe95b42d1c4, 0x30d76d6f03d315b9}},
+ {{0x2eccdd0e632f9c1d, 0x51d0b69676893115, 0x52dfb76ba8637a58, 0x6dd37d49a00eef39}},
+ {{0x6c4444172106e4c7, 0xfb53d680928d7f69, 0xb4739ea4694d3f26, 0x10c697112e864bb0}}},
+{{{0x6493c4277dbe5fde, 0x265d4fad19ad7ea2, 0x0e00dfc846304590, 0x25e61cabed66fe09}},
+ {{0x0ca62aa08358c805, 0x6a3d4ae37a204247, 0x7464d3a63b11eddc, 0x03bf9baf550806ef}},
+ {{0x3f13e128cc586604, 0x6f5873ecb459747e, 0xa0b63dedcc1268f5, 0x566d78634586e22c}}},
+{{{0x1637a49f9cc10834, 0xbc8e56d5a89bc451, 0x1cb5ec0f7f7fd2db, 0x33975bca5ecc35d9}},
+ {{0xa1054285c65a2fd0, 0x6c64112af31667c3, 0x680ae240731aee58, 0x14fba5f34793b22a}},
+ {{0x3cd746166985f7d4, 0x593e5e84c9c80057, 0x2fc3f2b67b61131e, 0x14829cea83fc526c}}},
+{{{0xff437b8497dd95c2, 0x6c744e30aa4eb5a7, 0x9e0c5d613c85e88b, 0x2fd9c71e5f758173}},
+ {{0x21e70b2f4e71ecb8, 0xe656ddb940a477e3, 0xbf6556cece1d4f80, 0x05fc3bc4535d7b7e}},
+ {{0x24b8b3ae52afdedd, 0x3495638ced3b30cf, 0x33a4bc83a9be8195, 0x373767475c651f04}}},
+{{{0x2fba99fd40d1add9, 0xb307166f96f4d027, 0x4363f05215f03bae, 0x1fbea56c3b18f999}},
+ {{0x634095cb14246590, 0xef12144016c15535, 0x9e38140c8910bc60, 0x6bf5905730907c8c}},
+ {{0x0fa778f1e1415b8a, 0x06409ff7bac3a77e, 0x6f52d7b89aa29a50, 0x02521cf67a635a56}}},
+{{{0x513fee0b0a9d5294, 0x8f98e75c0fdf5a66, 0xd4618688bfe107ce, 0x3fa00a7e71382ced}},
+ {{0xb1146720772f5ee4, 0xe8f894b196079ace, 0x4af8224d00ac824a, 0x001753d9f7cd6cc4}},
+ {{0x3c69232d963ddb34, 0x1dde87dab4973858, 0xaad7d1f9a091f285, 0x12b5fe2fa048edb6}}},
+{{{0x71f0fbc496fce34d, 0x73b9826badf35bed, 0xd2047261ff28c561, 0x749b76f96fb1206f}},
+ {{0xdf2b7c26ad6f1e92, 0x4b66d323504b8913, 0x8c409dc0751c8bc3, 0x6f7e93c20796c7b8}},
+ {{0x1f5af604aea6ae05, 0xc12351f1bee49c99, 0x61a808b5eeff6b66, 0x0fcec10f01e02151}}},
+{{{0x644d58a649fe1e44, 0x21fcaea231ad777e, 0x02441c5a887fd0d2, 0x4901aa7183c511f3}},
+ {{0x3df2d29dc4244e45, 0x2b020e7493d8de0a, 0x6cc8067e820c214d, 0x413779166feab90a}},
+ {{0x08b1b7548c1af8f0, 0xce0f7a7c246299b4, 0xf760b0f91e06d939, 0x41bb887b726d1213}}},
+{{{0x40e87d44744346be, 0x1d48dad415b52b25, 0x7c3a8a18a13b603e, 0x4eb728c12fcdbdf7}},
+ {{0x7e234c597c6691ae, 0x64889d3d0a85b4c8, 0xdae2c90c354afae7, 0x0a871e070c6a9e1d}},
+ {{0x3301b5994bbc8989, 0x736bae3a5bdd4260, 0x0d61ade219d59e3c, 0x3ee7300f2685d464}}},
+{{{0xf5d255e49e7dd6b7, 0x8016115c610b1eac, 0x3c99975d92e187ca, 0x13815762979125c2}},
+ {{0x43fa7947841e7518, 0xe5c6fa59639c46d7, 0xa1065e1de3052b74, 0x7d47c6a2cfb89030}},
+ {{0x3fdad0148ef0d6e0, 0x9d3e749a91546f3c, 0x71ec621026bb8157, 0x148cf58d34c9ec80}}},
+{{{0x46a492f67934f027, 0x469984bef6840aa9, 0x5ca1bc2a89611854, 0x3ff2fa1ebd5dbbd4}},
+ {{0xe2572f7d9ae4756d, 0x56c345bb88f3487f, 0x9fd10b6d6960a88d, 0x278febad4eaea1b9}},
+ {{0xb1aa681f8c933966, 0x8c21949c20290c98, 0x39115291219d3c52, 0x4104dd02fe9c677b}}},
+{{{0x72b2bf5e1124422a, 0xa1fa0c3398a33ab5, 0x94cb6101fa52b666, 0x2c863b00afaf53d5}},
+ {{0x81214e06db096ab8, 0x21a8b6c90ce44f35, 0x6524c12a409e2af5, 0x0165b5a48efca481}},
+ {{0xf190a474a0846a76, 0x12eff984cd2f7cc0, 0x695e290658aa2b8f, 0x591b67d9bffec8b8}}},
+{{{0x312f0d1c80b49bfa, 0x5979515eabf3ec8a, 0x727033c09ef01c88, 0x3de02ec7ca8f7bcb}},
+ {{0x99b9b3719f18b55d, 0xe465e5faa18c641e, 0x61081136c29f05ed, 0x489b4f867030128b}},
+ {{0xd232102d3aeb92ef, 0xe16253b46116a861, 0x3d7eabe7190baa24, 0x49f5fbba496cbebf}}},
+{{{0x30949a108a5bcfd4, 0xdc40dd70bc6473eb, 0x92c294c1307c0d1c, 0x5604a86dcbfa6e74}},
+ {{0x155d628c1e9c572e, 0x8a4d86acc5884741, 0x91a352f6515763eb, 0x06a1a6c28867515b}},
+ {{0x7288d1d47c1764b6, 0x72541140e0418b51, 0x9f031a6018acf6d1, 0x20989e89fe2742c6}}},
+{{{0x499777fd3a2dcc7f, 0x32857c2ca54fd892, 0xa279d864d207e3a0, 0x0403ed1d0ca67e29}},
+ {{0x1674278b85eaec2e, 0x5621dc077acb2bdf, 0x640a4c1661cbf45a, 0x730b9950f70595d3}},
+ {{0xc94b2d35874ec552, 0xc5e6c8cf98246f8d, 0xf7cb46fa16c035ce, 0x5bd7454308303dcc}}},
+{{{0x7f9ad19528b24cc2, 0x7f6b54656335c181, 0x66b8b66e4fc07236, 0x133a78007380ad83}},
+ {{0x85c4932115e7792a, 0xc64c89a2bdcdddc9, 0x9d1e3da8ada3d762, 0x5bb7db123067f82c}},
+ {{0x0961f467c6ca62be, 0x04ec21d6211952ee, 0x182360779bd54770, 0x740dca6d58f0e0d2}}},
+{{{0xdf48ee0752cfce4e, 0xc3fffaf306ec08b7, 0x05710b2ab95459c4, 0x161d25fa963ea38d}},
+ {{0x231a8c570478433c, 0xb7b5270ec281439d, 0xdbaa99eae3d9079f, 0x2c03f5256c2b03d9}},
+ {{0x790f18757b53a47d, 0x307b0130cf0c5879, 0x31903d77257ef7f9, 0x699468bdbd96bbaf}}},
+{{{0xbd1f2f46f4dafecf, 0x7cef0114a47fd6f7, 0xd31ffdda4a47b37f, 0x525219a473905785}},
+ {{0xd8dd3de66aa91948, 0x485064c22fc0d2cc, 0x9b48246634fdea2f, 0x293e1c4e6c4a2e3a}},
+ {{0x376e134b925112e1, 0x703778b5dca15da0, 0xb04589af461c3111, 0x5b605c447f032823}}},
+{{{0xb965805920c47c89, 0xe7f0100c923b8fcc, 0x0001256502e2ef77, 0x24a76dcea8aeb3ee}},
+ {{0x3be9fec6f0e7f04c, 0x866a579e75e34962, 0x5542ef161e1de61a, 0x2f12fef4cc5abdd5}},
+ {{0x0a4522b2dfc0c740, 0x10d06e7f40c9a407, 0xc6cf144178cff668, 0x5e607b2518a43790}}},
+{{{0x58b31d8f6cdf1818, 0x35cfa74fc36258a2, 0xe1b3ff4f66e61d6e, 0x5067acab6ccdd5f7}},
+ {{0xa02c431ca596cf14, 0xe3c42d40aed3e400, 0xd24526802e0f26db, 0x201f33139e457068}},
+ {{0xfd527f6b08039d51, 0x18b14964017c0006, 0xd5220eb02e25a4a8, 0x397cba8862460375}}},
+{{{0x30c13093f05959b2, 0xe23aa18de9a97976, 0x222fd491721d5e26, 0x2339d320766e6c3a}},
+ {{0x7815c3fbc81379e7, 0xa6619420dde12af1, 0xffa9c0f885a8fdd5, 0x771b4022c1e1c252}},
+ {{0xd87dd986513a2fa7, 0xf5ac9b71f9d4cf08, 0xd06bc31b1ea283b3, 0x331a189219971a76}}},
+{{{0xf5166f45fb4f80c6, 0x9c36c7de61c775cf, 0xe3d4e81b9041d91c, 0x31167c6b83bdfe21}},
+ {{0x26512f3a9d7572af, 0x5bcbe28868074a9e, 0x84edc1c11180f7c4, 0x1ac9619ff649a67b}},
+ {{0xf22b3842524b1068, 0x5068343bee9ce987, 0xfc9d71844a6250c8, 0x612436341f08b111}}},
+{{{0xd99d41db874e898d, 0x09fea5f16c07dc20, 0x793d2c67d00f9bbc, 0x46ebe2309e5eff40}},
+ {{0x8b6349e31a2d2638, 0x9ddfb7009bd3fd35, 0x7f8bf1b8a3a06ba4, 0x1522aa3178d90445}},
+ {{0x2c382f5369614938, 0xdafe409ab72d6d10, 0xe8c83391b646f227, 0x45fe70f50524306c}}},
+{{{0xda4875a6960c0b8c, 0x5b68d076ef0e2f20, 0x07fb51cf3d0b8fd4, 0x428d1623a0e392d4}},
+ {{0x62f24920c8951491, 0x05f007c83f630ca2, 0x6fbb45d2f5c9d4b8, 0x16619f6db57a2245}},
+ {{0x084f4a4401a308fd, 0xa82219c376a5caac, 0xdeb8de4643d1bc7d, 0x1d81592d60bd38c6}}},
+{{{0x61368756a60dac5f, 0x17e02f6aebabdc57, 0x7f193f2d4cce0f7d, 0x20234a7789ecdcf0}},
+ {{0x8765b69f7b85c5e8, 0x6ff0678bd168bab2, 0x3a70e77c1d330f9b, 0x3a5f6d51b0af8e7c}},
+ {{0x76d20db67178b252, 0x071c34f9d51ed160, 0xf62a4a20b3e41170, 0x7cd682353cffe366}}},
+{{{0x0be1a45bd887fab6, 0x2a846a32ba403b6e, 0xd9921012e96e6000, 0x2838c8863bdc0943}},
+ {{0xa665cd6068acf4f3, 0x42d92d183cd7e3d3, 0x5759389d336025d9, 0x3ef0253b2b2cd8ff}},
+ {{0xd16bb0cf4a465030, 0xfa496b4115c577ab, 0x82cfae8af4ab419d, 0x21dcb8a606a82812}}},
+{{{0x5c6004468c9d9fc8, 0x2540096ed42aa3cb, 0x125b4d4c12ee2f9c, 0x0bc3d08194a31dab}},
+ {{0x9a8d00fabe7731ba, 0x8203607e629e1889, 0xb2cc023743f3d97f, 0x5d840dbf6c6f678b}},
+ {{0x706e380d309fe18b, 0x6eb02da6b9e165c7, 0x57bbba997dae20ab, 0x3a4276232ac196dd}}},
+{{{0x4b42432c8a7084fa, 0x898a19e3dfb9e545, 0xbe9f00219c58e45d, 0x1ff177cea16debd1}},
+ {{0x3bf8c172db447ecb, 0x5fcfc41fc6282dbd, 0x80acffc075aa15fe, 0x0770c9e824e1a9f9}},
+ {{0xcf61d99a45b5b5fd, 0x860984e91b3a7924, 0xe7300919303e3e89, 0x39f264fd41500b1e}}},
+{{{0xa7ad3417dbe7e29c, 0xbd94376a2b9c139c, 0xa0e91b8e93597ba9, 0x1712d73468889840}},
+ {{0xd19b4aabfe097be1, 0xa46dfce1dfe01929, 0xc3c908942ca6f1ff, 0x65c621272c35f14e}},
+ {{0xe72b89f8ce3193dd, 0x4d103356a125c0bb, 0x0419a93d2e1cfe83, 0x22f9800ab19ce272}}},
+{{{0x605a368a3e9ef8cb, 0xe3e9c022a5504715, 0x553d48b05f24248f, 0x13f416cd647626e5}},
+ {{0x42029fdd9a6efdac, 0xb912cebe34a54941, 0x640f64b987bdf37b, 0x4171a4d38598cab4}},
+ {{0xfa2758aa99c94c8c, 0x23006f6fb000b807, 0xfbd291ddadda5392, 0x508214fa574bd1ab}}},
+{{{0xc20269153ed6fe4b, 0xa65a6739511d77c4, 0xcbde26462c14af94, 0x22f960ec6faba74b}},
+ {{0x461a15bb53d003d6, 0xb2102888bcf3c965, 0x27c576756c683a5a, 0x3a7758a4c86cb447}},
+ {{0x548111f693ae5076, 0x1dae21df1dfd54a6, 0x12248c90f3115e65, 0x5d9fd15f8de7f494}}},
+{{{0x031408d36d63727f, 0x6a379aefd7c7b533, 0xa9e18fc5ccaee24b, 0x332f35914f8fbed3}},
+ {{0x3f244d2aeed7521e, 0x8e3a9028432e9615, 0xe164ba772e9c16d4, 0x3bc187fa47eb98d8}},
+ {{0x6d470115ea86c20c, 0x998ab7cb6c46d125, 0xd77832b53a660188, 0x450d81ce906fba03}}},
+{{{0x6e7bb6a1a6205275, 0xaa4f21d7413c8e83, 0x6f56d155e88f5cb2, 0x2de25d4ba6345be1}},
+ {{0xd074d8961cae743f, 0xf86d18f5ee1c63ed, 0x97bdc55be7f4ed29, 0x4cbad279663ab108}},
+ {{0x80d19024a0d71fcd, 0xc525c20afb288af8, 0xb1a3974b5f3a6419, 0x7d7fbcefe2007233}}},
+{{{0xfaef1e6a266b2801, 0x866c68c4d5739f16, 0xf68a2fbc1b03762c, 0x5975435e87b75a8d}},
+ {{0xcd7c5dc5f3c29094, 0xc781a29a2a9105ab, 0x80c61d36421c3058, 0x4f9cd196dcd8d4d7}},
+ {{0x199297d86a7b3768, 0xd0d058241ad17a63, 0xba029cad5c1c0c17, 0x7ccdd084387a0307}}},
+{{{0xdca6422c6d260417, 0xae153d50948240bd, 0xa9c0c1b4fb68c677, 0x428bd0ed61d0cf53}},
+ {{0x9b0c84186760cc93, 0xcdae007a1ab32a99, 0xa88dec86620bda18, 0x3593ca848190ca44}},
+ {{0x9213189a5e849aa7, 0xd4d8c33565d8facd, 0x8c52545b53fdbbd1, 0x27398308da2d63e6}}},
+{{{0x42c38d28435ed413, 0xbd50f3603278ccc9, 0xbb07ab1a79da03ef, 0x269597aebe8c3355}},
+ {{0xb9a10e4c0a702453, 0x0fa25866d57d1bde, 0xffb9d9b5cd27daf7, 0x572c2945492c33fd}},
+ {{0xc77fc745d6cd30be, 0xe4dfe8d3e3baaefb, 0xa22c8830aa5dda0c, 0x7f985498c05bca80}}},
+{{{0x3849ce889f0be117, 0x8005ad1b7b54a288, 0x3da3c39f23fc921c, 0x76c2ec470a31f304}},
+ {{0xd35615520fbf6363, 0x08045a45cf4dfba6, 0xeec24fbc873fa0c2, 0x30f2653cd69b12e7}},
+ {{0x8a08c938aac10c85, 0x46179b60db276bcb, 0xa920c01e0e6fac70, 0x2f1273f1596473da}}},
+{{{0x4739fc7c8ae01e11, 0xfd5274904a6aab9f, 0x41d98a8287728f2e, 0x5d9e572ad85b69f2}},
+ {{0x30488bd755a70bc0, 0x06d6b5a4f1d442e7, 0xead1a69ebc596162, 0x38ac1997edc5f784}},
+ {{0x0666b517a751b13b, 0x747d06867e9b858c, 0xacacc011454dde49, 0x22dfcd9cbfe9e69c}}},
+{{{0x8ddbd2e0c30d0cd9, 0xad8e665facbb4333, 0x8f6b258c322a961f, 0x6b2916c05448c1c7}},
+ {{0x56ec59b4103be0a1, 0x2ee3baecd259f969, 0x797cb29413f5cd32, 0x0fe9877824cde472}},
+ {{0x7edb34d10aba913b, 0x4ea3cd822e6dac0e, 0x66083dff6578f815, 0x4c303f307ff00a17}}},
+{{{0xd30a3bd617b28c85, 0xc5d377b739773bea, 0xc6c6e78c1e6a5cbf, 0x0d61b8f78b2ab7c4}},
+ {{0x29fc03580dd94500, 0xecd27aa46fbbec93, 0x130a155fc2e2a7f8, 0x416b151ab706a1d5}},
+ {{0x56a8d7efe9c136b0, 0xbd07e5cd58e44b20, 0xafe62fda1b57e0ab, 0x191a2af74277e8d2}}},
+{{{0xce16f74bc53c1431, 0x2b9725ce2072edde, 0xb8b9c36fb5b23ee7, 0x7e2e0e450b5cc908}},
+ {{0x9fe62b434f460efb, 0xded303d4a63607d6, 0xf052210eb7a0da24, 0x237e7dbe00545b93}},
+ {{0x013575ed6701b430, 0x231094e69f0bfd10, 0x75320f1583e47f22, 0x71afa699b11155e3}}},
+{{{0x65ce6f9b3953b61d, 0xc65839eaafa141e6, 0x0f435ffda9f759fe, 0x021142e9c2b1c28e}},
+ {{0xea423c1c473b50d6, 0x51e87a1f3b38ef10, 0x9b84bf5fb2c9be95, 0x00731fbc78f89a1c}},
+ {{0xe430c71848f81880, 0xbf960c225ecec119, 0xb6dae0836bba15e3, 0x4c4d6f3347e15808}}},
+{{{0x18f7eccfc17d1fc9, 0x6c75f5a651403c14, 0xdbde712bf7ee0cdf, 0x193fddaaa7e47a22}},
+ {{0x2f0cddfc988f1970, 0x6b916227b0b9f51b, 0x6ec7b6c4779176be, 0x38bf9500a88f9fa8}},
+ {{0x1fd2c93c37e8876f, 0xa2f61e5a18d1462c, 0x5080f58239241276, 0x6a6fb99ebf0d4969}}},
+{{{0x6a46c1bb560855eb, 0x2416bb38f893f09d, 0xd71d11378f71acc1, 0x75f76914a31896ea}},
+ {{0xeeb122b5b6e423c6, 0x939d7010f286ff8e, 0x90a92a831dcf5d8c, 0x136fda9f42c5eb10}},
+ {{0xf94cdfb1a305bdd1, 0x0f364b9d9ff82c08, 0x2a87d8a5c3bb588a, 0x022183510be8dcba}}},
+{{{0x4af766385ead2d14, 0xa08ed880ca7c5830, 0x0d13a6e610211e3d, 0x6a071ce17b806c03}},
+ {{0x9d5a710143307a7f, 0xb063de9ec47da45f, 0x22bbfe52be927ad3, 0x1387c441fd40426c}},
+ {{0xb5d3c3d187978af8, 0x722b5a3d7f0e4413, 0x0d7b4848bb477ca0, 0x3171b26aaf1edc92}}},
+{{{0xa92f319097564ca8, 0xff7bb84c2275e119, 0x4f55fe37a4875150, 0x221fd4873cf0835a}},
+ {{0xa60db7d8b28a47d1, 0xa6bf14d61770a4f1, 0xd4a1f89353ddbd58, 0x6c514a63344243e9}},
+ {{0x2322204f3a156341, 0xfb73e0e9ba0a032d, 0xfce0dd4c410f030e, 0x48daa596fb924aaa}}},
+{{{0x6eca8e665ca59cc7, 0xa847254b2e38aca0, 0x31afc708d21e17ce, 0x676dd6fccad84af7}},
+ {{0x14f61d5dc84c9793, 0x9941f9e3ef418206, 0xcdf5b88f346277ac, 0x58c837fa0e8a79a9}},
+ {{0x0cf9688596fc9058, 0x1ddcbbf37b56a01b, 0xdcc2e77d4935d66a, 0x1c4f73f2c6a57f0a}}},
+{{{0x0e7a4fbd305fa0bb, 0x829d4ce054c663ad, 0xf421c3832fe33848, 0x795ac80d1bf64c42}},
+ {{0xb36e706efc7c3484, 0x73dfc9b4c3c1cf61, 0xeb1d79c9781cc7e5, 0x70459adb7daf675c}},
+ {{0x1b91db4991b42bb3, 0x572696234b02dcca, 0x9fdf9ee51f8c78dc, 0x5fe162848ce21fd3}}},
+{{{0x4e59214fe194961a, 0x49be7dc70d71cd4f, 0x9300cfd23b50f22d, 0x4789d446fc917232}},
+ {{0x2879852d5d7cb208, 0xb8dedd70687df2e7, 0xdc0bffab21687891, 0x2b44c043677daa35}},
+ {{0x1a1c87ab074eb78e, 0xfac6d18e99daf467, 0x3eacbbcd484f9067, 0x60c52eef2bb9a4e4}}},
+{{{0x0b5d89bc3bfd8bf1, 0xb06b9237c9f3551a, 0x0e4c16b0d53028f5, 0x10bc9c312ccfcaab}},
+ {{0x702bc5c27cae6d11, 0x44c7699b54a48cab, 0xefbc4056ba492eb2, 0x70d77248d9b6676d}},
+ {{0xaa8ae84b3ec2a05b, 0x98699ef4ed1781e0, 0x794513e4708e85d1, 0x63755bd3a976f413}}},
+{{{0xb55fa03e2ad10853, 0x356f75909ee63569, 0x9ff9f1fdbe69b890, 0x0d8cc1c48bc16f84}},
+ {{0x3dc7101897f1acb7, 0x5dda7d5ec165bbd8, 0x508e5b9c0fa1020f, 0x2763751737c52a56}},
+ {{0x029402d36eb419a9, 0xf0b44e7e77b460a5, 0xcfa86230d43c4956, 0x70c2dd8a7ad166e7}}},
+{{{0x656194509f6fec0e, 0xee2e7ea946c6518d, 0x9733c1f367e09b5c, 0x2e0fac6363948495}},
+ {{0x91d4967db8ed7e13, 0x74252f0ad776817a, 0xe40982e00d852564, 0x32b8613816a53ce5}},
+ {{0x79e7f7bee448cd64, 0x6ac83a67087886d0, 0xf89fd4d9a0e4db2e, 0x4179215c735a4f41}}},
+{{{0x8c7094e7d7dced2a, 0x97fb8ac347d39c70, 0xe13be033a906d902, 0x700344a30cd99d76}},
+ {{0xe4ae33b9286bcd34, 0xb7ef7eb6559dd6dc, 0x278b141fb3d38e1f, 0x31fa85662241c286}},
+ {{0xaf826c422e3622f4, 0xc12029879833502d, 0x9bc1b7e12b389123, 0x24bb2312a9952489}}},
+{{{0xb1a8ed1732de67c3, 0x3cb49418461b4948, 0x8ebd434376cfbcd2, 0x0fee3e871e188008}},
+ {{0x41f80c2af5f85c6b, 0x687284c304fa6794, 0x8945df99a3ba1bad, 0x0d1d2af9ffeb5d16}},
+ {{0xa9da8aa132621edf, 0x30b822a159226579, 0x4004197ba79ac193, 0x16acd79718531d76}}},
+{{{0x72df72af2d9b1d3d, 0x63462a36a432245a, 0x3ecea07916b39637, 0x123e0ef6b9302309}},
+ {{0xc959c6c57887b6ad, 0x94e19ead5f90feba, 0x16e24e62a342f504, 0x164ed34b18161700}},
+ {{0x487ed94c192fe69a, 0x61ae2cea3a911513, 0x877bf6d3b9a4de27, 0x78da0fc61073f3eb}}},
+{{{0x5bf15d28e52bc66a, 0x2c47e31870f01a8e, 0x2419afbc06c28bdd, 0x2d25deeb256b173a}},
+ {{0xa29f80f1680c3a94, 0x71f77e151ae9e7e6, 0x1100f15848017973, 0x054aa4b316b38ddd}},
+ {{0xdfc8468d19267cb8, 0x0b28789c66e54daf, 0x2aeb1d2a666eec17, 0x134610a6ab7da760}}},
+{{{0x51138ec78df6b0fe, 0x5397da89e575f51b, 0x09207a1d717af1b9, 0x2102fdba2b20d650}},
+ {{0xcd2a65e777d1f515, 0x548991878faa60f1, 0xb1b73bbcdabc06e5, 0x654878cba97cc9fb}},
+ {{0x969ee405055ce6a1, 0x36bca7681251ad29, 0x3a1af517aa7da415, 0x0ad725db29ecb2ba}}},
+{{{0xdc4267b1834e2457, 0xb67544b570ce1bc5, 0x1af07a0bf7d15ed7, 0x4aefcffb71a03650}},
+ {{0xfec7bc0c9b056f85, 0x537d5268e7f5ffd7, 0x77afc6624312aefa, 0x4f675f5302399fd9}},
+ {{0xc32d36360415171e, 0xcd2bef118998483b, 0x870a6eadd0945110, 0x0bccbb72a2a86561}}},
+{{{0x185e962feab1a9c8, 0x86e7e63565147dcd, 0xb092e031bb5b6df2, 0x4024f0ab59d6b73e}},
+ {{0x186d5e4c50fe1296, 0xe0397b82fee89f7e, 0x3bc7f6c5507031b0, 0x6678fd69108f37c2}},
+ {{0x1586fa31636863c2, 0x07f68c48572d33f2, 0x4f73cc9f789eaefc, 0x2d42e2108ead4701}}},
+{{{0x97f5131594dfd29b, 0x6155985d313f4c6a, 0xeba13f0708455010, 0x676b2608b8d2d322}},
+ {{0x21717b0d0f537593, 0x914e690b131e064c, 0x1bb687ae752ae09f, 0x420bf3a79b423c6e}},
+ {{0x8138ba651c5b2b47, 0x8671b6ec311b1b80, 0x7bff0cb1bc3135b0, 0x745d2ffa9c0cf1e0}}},
+{{{0xbf525a1e2bc9c8bd, 0xea5b260826479d81, 0xd511c70edf0155db, 0x1ae23ceb960cf5d0}},
+ {{0x6036df5721d34e6a, 0xb1db8827997bb3d0, 0xd3c209c3c8756afa, 0x06e15be54c1dc839}},
+ {{0x5b725d871932994a, 0x32351cb5ceb1dab0, 0x7dc41549dab7ca05, 0x58ded861278ec1f7}}},
+{{{0xd8173793f266c55c, 0xc8c976c5cc454e49, 0x5ce382f8bc26c3a8, 0x2ff39de85485f6f9}},
+ {{0x2dfb5ba8b6c2c9a8, 0x48eeef8ef52c598c, 0x33809107f12d1573, 0x08ba696b531d5bd8}},
+ {{0x77ed3eeec3efc57a, 0x04e05517d4ff4811, 0xea3d7a3ff1a671cb, 0x120633b4947cfe54}}},
+{{{0x0b94987891610042, 0x4ee7b13cecebfae8, 0x70be739594f0a4c0, 0x35d30a99b4d59185}},
+ {{0x82bd31474912100a, 0xde237b6d7e6fbe06, 0xe11e761911ea79c6, 0x07433be3cb393bde}},
+ {{0xff7944c05ce997f4, 0x575d3de4b05c51a3, 0x583381fd5a76847c, 0x2d873ede7af6da9f}}},
+{{{0x157a316443373409, 0xfab8b7eef4aa81d9, 0xb093fee6f5a64806, 0x2e773654707fa7b6}},
+ {{0xaa6202e14e5df981, 0xa20d59175015e1f5, 0x18a275d3bae21d6c, 0x0543618a01600253}},
+ {{0x0deabdf4974c23c1, 0xaa6f0a259dce4693, 0x04202cb8a29aba2c, 0x4b1443362d07960d}}},
+{{{0xccc4b7c7b66e1f7a, 0x44157e25f50c2f7e, 0x3ef06dfc713eaf1c, 0x582f446752da63f7}},
+ {{0x967c54e91c529ccb, 0x30f6269264c635fb, 0x2747aff478121965, 0x17038418eaf66f5c}},
+ {{0xc6317bd320324ce4, 0xa81042e8a4488bc4, 0xb21ef18b4e5a1364, 0x0c2a1c4bcda28dc9}}},
+{{{0xd24dc7d06f1f0447, 0xb2269e3edb87c059, 0xd15b0272fbb2d28f, 0x7c558bd1c6f64877}},
+ {{0xedc4814869bd6945, 0x0d6d907dbe1c8d22, 0xc63bd212d55cc5ab, 0x5a6a9b30a314dc83}},
+ {{0xd0ec1524d396463d, 0x12bb628ac35a24f0, 0xa50c3a791cbc5fa4, 0x0404a5ca0afbafc3}}},
+{{{0x8c1f40070aa743d6, 0xccbad0cb5b265ee8, 0x574b046b668fd2de, 0x46395bfdcadd9633}},
+ {{0x62bc9e1b2a416fd1, 0xb5c6f728e350598b, 0x04343fd83d5d6967, 0x39527516e7f8ee98}},
+ {{0x117fdb2d1a5d9a9c, 0x9c7745bcd1005c2a, 0xefd4bef154d56fea, 0x76579a29e822d016}}},
+{{{0x45b68e7e49c02a17, 0x23cd51a2bca9a37f, 0x3ed65f11ec224c1b, 0x43a384dc9e05bdb1}},
+ {{0x333cb51352b434f2, 0xd832284993de80e1, 0xb5512887750d35ce, 0x02c514bb2a2777c1}},
+ {{0x684bd5da8bf1b645, 0xfb8bd37ef6b54b53, 0x313916d7a9b0d253, 0x1160920961548059}}},
+{{{0xb44d166929dacfaa, 0xda529f4c8413598f, 0xe9ef63ca453d5559, 0x351e125bc5698e0b}},
+ {{0x7a385616369b4dcd, 0x75c02ca7655c3563, 0x7dc21bf9d4f18021, 0x2f637d7491e6e042}},
+ {{0xd4b49b461af67bbe, 0xd603037ac8ab8961, 0x71dee19ff9a699fb, 0x7f182d06e7ce2a9a}}},
+{{{0x7a7c8e64ab0168ec, 0xcb5a4a5515edc543, 0x095519d347cd0eda, 0x67d4ac8c343e93b0}},
+ {{0x09454b728e217522, 0xaa58e8f4d484b8d8, 0xd358254d7f46903c, 0x44acc043241c5217}},
+ {{0x1c7d6bbb4f7a5777, 0x8b35fed4918313e1, 0x4adca1c6c96b4684, 0x556d1c8312ad71bd}}},
+{{{0x17ef40e30c8d3982, 0x31f7073e15a3fa34, 0x4f21f3cb0773646e, 0x746c6c6d1d824eff}},
+ {{0x81f06756b11be821, 0x0faff82310a3f3dd, 0xf8b2d0556a99465d, 0x097abe38cc8c7f05}},
+ {{0x0c49c9877ea52da4, 0x4c4369559bdc1d43, 0x022c3809f7ccebd2, 0x577e14a34bee84bd}}},
+{{{0xf0e268ac61a73b0a, 0xf2fafa103791a5f5, 0xc1e13e826b6d00e9, 0x60fa7ee96fd78f42}},
+ {{0x94fecebebd4dd72b, 0xf46a4fda060f2211, 0x124a5977c0c8d1ff, 0x705304b8fb009295}},
+ {{0xb63d1d354d296ec6, 0xf3c3053e5fad31d8, 0x670b958cb4bd42ec, 0x21398e0ca16353fd}}},
+{{{0x89f5058a382b33f3, 0x5ae2ba0bad48c0b4, 0x8f93b503a53db36e, 0x5aa3ed9d95a232e6}},
+ {{0x2798aaf9b4b75601, 0x5eac72135c8dad72, 0xd2ceaa6161b7a023, 0x1bbfb284e98f7d4e}},
+ {{0x656777e9c7d96561, 0xcb2b125472c78036, 0x65053299d9506eee, 0x4a07e14e5e8957cc}}},
+{{{0x4ee412cb980df999, 0xa315d76f3c6ec771, 0xbba5edde925c77fd, 0x3f0bac391d313402}},
+ {{0x240b58cdc477a49b, 0xfd38dade6447f017, 0x19928d32a7c86aad, 0x50af7aed84afa081}},
+ {{0x6e4fde0115f65be5, 0x29982621216109b2, 0x780205810badd6d9, 0x1921a316baebd006}}},
+{{{0x89422f7edfb870fc, 0x2c296beb4f76b3bd, 0x0738f1d436c24df7, 0x6458df41e273aeb0}},
+ {{0xd75aad9ad9f3c18b, 0x566a0eef60b1c19c, 0x3e9a0bac255c0ed9, 0x7b049deca062c7f5}},
+ {{0xdccbe37a35444483, 0x758879330fedbe93, 0x786004c312c5dd87, 0x6093dccbc2950e64}}},
+{{{0x1ff39a8585e0706d, 0x36d0a5d8b3e73933, 0x43b9f2e1718f453b, 0x57d1ea084827a97c}},
+ {{0x6bdeeebe6084034b, 0x3199c2b6780fb854, 0x973376abb62d0695, 0x6e3180c98b647d90}},
+ {{0xee7ab6e7a128b071, 0xa4c1596d93a88baa, 0xf7b4de82b2216130, 0x363e999ddd97bd18}}},
+{{{0x96a843c135ee1fc4, 0x976eb35508e4c8cf, 0xb42f6801b58cd330, 0x48ee9b78693a052b}},
+ {{0x2f1848dce24baec6, 0x769b7255babcaf60, 0x90cb3c6e3cefe931, 0x231f979bc6f9b355}},
+ {{0x5c31de4bcc2af3c6, 0xb04bb030fe208d1f, 0xb78d7009c14fb466, 0x079bfa9b08792413}}},
+{{{0xe3903a51da300df4, 0x843964233da95ab0, 0xed3cf12d0b356480, 0x038c77f684817194}},
+ {{0xf3c9ed80a2d54245, 0x0aa08b7877f63952, 0xd76dac63d1085475, 0x1ef4fb159470636b}},
+ {{0x854e5ee65b167bec, 0x59590a4296d0cdc2, 0x72b2df3498102199, 0x575ee92a4a0bff56}}},
+{{{0xd4c080908a182fcf, 0x30e170c299489dbd, 0x05babd5752f733de, 0x43d4e7112cd3fd00}},
+ {{0x5d46bc450aa4d801, 0xc3af1227a533b9d8, 0x389e3b262b8906c2, 0x200a1e7e382f581b}},
+ {{0x518db967eaf93ac5, 0x71bc989b056652c0, 0xfe2b85d9567197f5, 0x050eca52651e4e38}}},
+{{{0xc3431ade453f0c9c, 0xe9f5045eff703b9b, 0xfcd97ac9ed847b3d, 0x4b0ee6c21c58f4c6}},
+ {{0x97ac397660e668ea, 0x9b19bbfe153ab497, 0x4cb179b534eca79f, 0x6151c09fa131ae57}},
+ {{0x3af55c0dfdf05d96, 0xdd262ee02ab4ee7a, 0x11b2bb8712171709, 0x1fef24fa800f030b}}},
+{{{0x37d653fb1aa73196, 0x0f9495303fd76418, 0xad200b09fb3a17b2, 0x544d49292fc8613e}},
+ {{0x22d2aff530976b86, 0x8d90b806c2d24604, 0xdca1896c4de5bae5, 0x28005fe6c8340c17}},
+ {{0x6aefba9f34528688, 0x5c1bff9425107da1, 0xf75bbbcd66d94b36, 0x72e472930f316dfa}}},
+{{{0x2695208c9781084f, 0xb1502a0b23450ee1, 0xfd9daea603efde02, 0x5a9d2e8c2733a34c}},
+ {{0x07f3f635d32a7627, 0x7aaa4d865f6566f0, 0x3c85e79728d04450, 0x1fee7f000fe06438}},
+ {{0x765305da03dbf7e5, 0xa4daf2491434cdbd, 0x7b4ad5cdd24a88ec, 0x00f94051ee040543}}},
+{{{0x8d356b23c3d330b2, 0xf21c8b9bb0471b06, 0xb36c316c6e42b83c, 0x07d79c7e8beab10d}},
+ {{0xd7ef93bb07af9753, 0x583ed0cf3db766a7, 0xce6998bf6e0b1ec5, 0x47b7ffd25dd40452}},
+ {{0x87fbfb9cbc08dd12, 0x8a066b3ae1eec29b, 0x0d57242bdb1fc1bf, 0x1c3520a35ea64bb6}}},
+{{{0x80d253a6bccba34a, 0x3e61c3a13838219b, 0x90c3b6019882e396, 0x1c3d05775d0ee66f}},
+ {{0xcda86f40216bc059, 0x1fbb231d12bcd87e, 0xb4956a9e17c70990, 0x38750c3b66d12e55}},
+ {{0x692ef1409422e51a, 0xcbc0c73c2b5df671, 0x21014fe7744ce029, 0x0621e2c7d330487c}}},
+{{{0xaf9860cc8259838d, 0x90ea48c1c69f9adc, 0x6526483765581e30, 0x0007d6097bd3a5bc}},
+ {{0xb7ae1796b0dbf0f3, 0x54dfafb9e17ce196, 0x25923071e9aaa3b4, 0x5d8e589ca1002e9d}},
+ {{0xc0bf1d950842a94b, 0xb2d3c363588f2e3e, 0x0a961438bb51e2ef, 0x1583d7783c1cbf86}}},
+{{{0xeceea2ef5da27ae1, 0x597c3a1455670174, 0xc9a62a126609167a, 0x252a5f2e81ed8f70}},
+ {{0x90034704cc9d28c7, 0x1d1b679ef72cc58f, 0x16e12b5fbe5b8726, 0x4958064e83c5580a}},
+ {{0x0d2894265066e80d, 0xfcc3f785307c8c6b, 0x1b53da780c1112fd, 0x079c170bd843b388}}},
+{{{0x0506ece464fa6fff, 0xbee3431e6205e523, 0x3579422451b8ea42, 0x6dec05e34ac9fb00}},
+ {{0xcdd6cd50c0d5d056, 0x9af7686dbb03573b, 0x3ca6723ff3c3ef48, 0x6768c0d7317b8acc}},
+ {{0x94b625e5f155c1b3, 0x417bf3a7997b7b91, 0xc22cbddc6d6b2600, 0x51445e14ddcd52f4}}},
+{{{0x57502b4b3b144951, 0x8e67ff6b444bbcb3, 0xb8bd6927166385db, 0x13186f31e39295c8}},
+ {{0x893147ab2bbea455, 0x8c53a24f92079129, 0x4b49f948be30f7a7, 0x12e990086e4fd43d}},
+ {{0xf10c96b37fdfbb2e, 0x9f9a935e121ceaf9, 0xdf1136c43a5b983f, 0x77b2e3f05d3e99af}}},
+{{{0x296fa9c59c2ec4de, 0xbc8b61bf4f84f3cb, 0x1c7706d917a8f908, 0x63b795fc7ad3255d}},
+ {{0xd598639c12ddb0a4, 0xa5d19f30c024866b, 0xd17c2f0358fce460, 0x07a195152e095e8a}},
+ {{0xa8368f02389e5fc8, 0x90433b02cf8de43b, 0xafa1fd5dc5412643, 0x3e8fe83d032f0137}}},
+{{{0x2f8b15b90570a294, 0x94f2427067084549, 0xde1c5ae161bbfd84, 0x75ba3b797fac4007}},
+ {{0x08704c8de8efd13c, 0xdfc51a8e33e03731, 0xa59d5da51260cde3, 0x22d60899a6258c86}},
+ {{0x6239dbc070cdd196, 0x60fe8a8b6c7d8a9a, 0xb38847bceb401260, 0x0904d07b87779e5e}}},
+{{{0xb4ce1fd4ddba919c, 0xcf31db3ec74c8daa, 0x2c63cc63ad86cc51, 0x43e2143fbc1dde07}},
+ {{0xf4322d6648f940b9, 0x06952f0cbd2d0c39, 0x167697ada081f931, 0x6240aacebaf72a6c}},
+ {{0xf834749c5ba295a0, 0xd6947c5bca37d25a, 0x66f13ba7e7c9316a, 0x56bdaf238db40cac}}},
+{{{0x362ab9e3f53533eb, 0x338568d56eb93d40, 0x9e0e14521d5a5572, 0x1d24a86d83741318}},
+ {{0x1310d36cc19d3bb2, 0x062a6bb7622386b9, 0x7c9b8591d7a14f5c, 0x03aa31507e1e5754}},
+ {{0xf4ec7648ffd4ce1f, 0xe045eaf054ac8c1c, 0x88d225821d09357c, 0x43b261dc9aeb4859}}},
+{{{0xe55b1e1988bb79bb, 0xa09ed07dc17a359d, 0xb02c2ee2603dea33, 0x326055cf5b276bc2}},
+ {{0x19513d8b6c951364, 0x94fe7126000bf47b, 0x028d10ddd54f9567, 0x02b4d5e242940964}},
+ {{0xb4a155cb28d18df2, 0xeacc4646186ce508, 0xc49cf4936c824389, 0x27a6c809ae5d3410}}},
+{{{0x8ba6ebcd1f0db188, 0x37d3d73a675a5be8, 0xf22edfa315f5585a, 0x2cb67174ff60a17e}},
+ {{0xcd2c270ac43d6954, 0xdd4a3e576a66cab2, 0x79fa592469d7036c, 0x221503603d8c2599}},
+ {{0x59eecdf9390be1d0, 0xa9422044728ce3f1, 0x82891c667a94f0f4, 0x7b1df4b73890f436}}},
+{{{0xe492f2e0b3b2a224, 0x7c6c9e062b551160, 0x15eb8fe20d7f7b0e, 0x61fcef2658fc5992}},
+ {{0x5f2e221807f8f58c, 0xe3555c9fd49409d4, 0xb2aaa88d1fb6a630, 0x68698245d352e03d}},
+ {{0xdbb15d852a18187a, 0xf3e4aad386ddacd7, 0x44bae2810ff6c482, 0x46cf4c473daf01cf}}},
+{{{0x426525ed9ec4e5f9, 0x0e5eda0116903303, 0x72b1a7f2cbe5cadc, 0x29387bcd14eb5f40}},
+ {{0x213c6ea7f1498140, 0x7c1e7ef8392b4854, 0x2488c38c5629ceba, 0x1065aae50d8cc5bb}},
+ {{0x1c2c4525df200d57, 0x5c3b2dd6bfca674a, 0x0a07e7b1e1834030, 0x69a198e64f1ce716}}},
+{{{0x9062b2e0d91a78bc, 0x47c9889cc8509667, 0x9df54a66405070b8, 0x7369e6a92493a1bf}},
+ {{0xe1014434dcc5caed, 0x47ed5d963c84fb33, 0x70019576ed86a0e7, 0x25b2697bd267f9e4}},
+ {{0x9d673ffb13986864, 0x3ca5fbd9415dc7b8, 0xe04ecc3bdf273b5e, 0x1420683db54e4cd2}}},
+{{{0xb478bd1e249dd197, 0x620c35005e58c102, 0xfb02d32fccbaac5c, 0x60b63bebf508a72d}},
+ {{0x34eebb6fc1cc5ad0, 0x6a1b0ce99646ac8b, 0xd3b0da49a66bde53, 0x31e83b4161d081c1}},
+ {{0x97e8c7129e062b4f, 0x49e48f4f29320ad8, 0x5bece14b6f18683f, 0x55cf1eb62d550317}}},
+{{{0x5879101065c23d58, 0x8b9d086d5094819c, 0xe2402fa912c55fa7, 0x669a6564570891d4}},
+ {{0x3076b5e37df58c52, 0xd73ab9dde799cc36, 0xbd831ce34913ee20, 0x1a56fbaa62ba0133}},
+ {{0x943e6b505c9dc9ec, 0x302557bba77c371a, 0x9873ae5641347651, 0x13c4836799c58a5c}}},
+{{{0x423a5d465ab3e1b9, 0xfc13c187c7f13f61, 0x19f83664ecb5b9b6, 0x66f80c93a637b607}},
+ {{0xc4dcfb6a5d8bd080, 0xdeebc4ec571a4842, 0xd4b2e883b8e55365, 0x50bdc87dc8e5b827}},
+ {{0x606d37836edfe111, 0x32353e15f011abd9, 0x64b03ac325b73b96, 0x1dd56444725fd5ae}}},
+{{{0x8fa47ff83362127d, 0xbc9f6ac471cd7c15, 0x6e71454349220c8b, 0x0e645912219f732e}},
+ {{0xc297e60008bac89a, 0x7d4cea11eae1c3e0, 0xf3e38be19fe7977c, 0x3a3a450f63a305cd}},
+ {{0x078f2f31d8394627, 0x389d3183de94a510, 0xd1e36c6d17996f80, 0x318c8d9393a9a87b}}},
+{{{0xf2745d032afffe19, 0x0c9f3c497f24db66, 0xbc98d3e3ba8598ef, 0x224c7c679a1d5314}},
+ {{0x5d669e29ab1dd398, 0xfc921658342d9e3b, 0x55851dfdf35973cd, 0x509a41c325950af6}},
+ {{0xbdc06edca6f925e9, 0x793ef3f4641b1f33, 0x82ec12809d833e89, 0x05bff02328a11389}}},
+{{{0x3632137023cae00b, 0x544acf0ad1accf59, 0x96741049d21a1c88, 0x780b8cc3fa2a44a7}},
+ {{0x6881a0dd0dc512e4, 0x4fe70dc844a5fafe, 0x1f748e6b8f4a5240, 0x576277cdee01a3ea}},
+ {{0x1ef38abc234f305f, 0x9a577fbd1405de08, 0x5e82a51434e62a0d, 0x5ff418726271b7a1}}},
+{{{0x398e080c1789db9d, 0xa7602025f3e778f5, 0xfa98894c06bd035d, 0x106a03dc25a966be}},
+ {{0xe5db47e813b69540, 0xf35d2a3b432610e1, 0xac1f26e938781276, 0x29d4db8ca0a0cb69}},
+ {{0xd9ad0aaf333353d0, 0x38669da5acd309e5, 0x3c57658ac888f7f0, 0x4ab38a51052cbefa}}},
+{{{0xda7c2b256768d593, 0x98c1c0574422ca13, 0xf1a80bd5ca0ace1d, 0x29cdd1adc088a690}},
+ {{0xd6cfd1ef5fddc09c, 0xe82b3efdf7575dce, 0x25d56b5d201634c2, 0x3041c6bb04ed2b9b}},
+ {{0x0ff2f2f9d956e148, 0xade797759f356b2e, 0x1a4698bb5f6c025c, 0x104bbd6814049a7b}}},
+{{{0x51f0fd3168f1ed67, 0x2c811dcdd86f3bc2, 0x44dc5c4304d2f2de, 0x5be8cc57092a7149}},
+ {{0xa95d9a5fd67ff163, 0xe92be69d4cc75681, 0xb7f8024cde20f257, 0x204f2a20fb072df5}},
+ {{0xc8143b3d30ebb079, 0x7589155abd652e30, 0x653c3c318f6d5c31, 0x2570fb17c279161f}}},
+{{{0x3efa367f2cb61575, 0xf5f96f761cd6026c, 0xe8c7142a65b52562, 0x3dcb65ea53030acd}},
+ {{0x192ea9550bb8245a, 0xc8e6fba88f9050d1, 0x7986ea2d88a4c935, 0x241c5f91de018668}},
+ {{0x28d8172940de6caa, 0x8fbf2cf022d9733a, 0x16d7fcdd235b01d1, 0x08420edd5fcdf0e5}}},
+{{{0xcdff20ab8362fa4a, 0x57e118d4e21a3e6e, 0xe3179617fc39e62b, 0x0d9a53efbc1769fd}},
+ {{0x0358c34e04f410ce, 0xb6135b5a276e0685, 0x5d9670c7ebb91521, 0x04d654f321db889c}},
+ {{0x5e7dc116ddbdb5d5, 0x2954deb68da5dd2d, 0x1cb608173334a292, 0x4a7a4f2618991ad7}}},
+{{{0xf4a718025fb15f95, 0x3df65f346b5c1b8f, 0xcdfcf08500e01112, 0x11b50c4cddd31848}},
+ {{0x24c3b291af372a4b, 0x93da8270718147f2, 0xdd84856486899ef2, 0x4a96314223e0ee33}},
+ {{0xa6e8274408a4ffd6, 0x738e177e9c1576d9, 0x773348b63d02b3f2, 0x4f4bce4dce6bcc51}}},
+{{{0xa71fce5ae2242584, 0x26ea725692f58a9e, 0xd21a09d71cea3cf4, 0x73fcdd14b71c01e6}},
+ {{0x30e2616ec49d0b6f, 0xe456718fcaec2317, 0x48eb409bf26b4fa6, 0x3042cee561595f37}},
+ {{0x427e7079449bac41, 0x855ae36dbce2310a, 0x4cae76215f841a7c, 0x389e740c9a9ce1d6}}},
+{{{0x64fcb3ae34dcb9ce, 0x97500323e348d0ad, 0x45b3f07d62c6381b, 0x61545379465a6788}},
+ {{0xc9bd78f6570eac28, 0xe55b0b3227919ce1, 0x65fc3eaba19b91ed, 0x25c425e5d6263690}},
+ {{0x3f3e06a6f1d7de6e, 0x3ef976278e062308, 0x8c14f6264e8a6c77, 0x6539a08915484759}}},
+{{{0xe9d21f74c3d2f773, 0xc150544125c46845, 0x624e5ce8f9b99e33, 0x11c5e4aac5cd186c}},
+ {{0xddc4dbd414bb4a19, 0x19b2bc3c98424f8e, 0x48a89fd736ca7169, 0x0f65320ef019bd90}},
+ {{0xd486d1b1cafde0c6, 0x4f3fe6e3163b5181, 0x59a8af0dfaf2939a, 0x4cabc7bdec33072a}}},
+{{{0x239e9624089c0a2e, 0xc748c4c03afe4738, 0x17dbed2a764fa12a, 0x639b93f0321c8582}},
+ {{0xc08f788f3f78d289, 0xfe30a72ca1404d9f, 0xf2778bfccf65cc9d, 0x7ee498165acb2021}},
+ {{0x7bd508e39111a1c3, 0x2b2b90d480907489, 0xe7d2aec2ae72fd19, 0x0edf493c85b602a6}}},
+{{{0xaecc8158599b5a68, 0xea574f0febade20e, 0x4fe41d7422b67f07, 0x403b92e3019d4fb4}},
+ {{0x6767c4d284764113, 0xa090403ff7f5f835, 0x1c8fcffacae6bede, 0x04c00c54d1dfa369}},
+ {{0x4dc22f818b465cf8, 0x71a0f35a1480eff8, 0xaee8bfad04c7d657, 0x355bb12ab26176f4}}},
+{{{0xa71e64cc7493bbf4, 0xe5bd84d9eca3b0c3, 0x0a6bc50cfa05e785, 0x0f9b8132182ec312}},
+ {{0xa301dac75a8c7318, 0xed90039db3ceaa11, 0x6f077cbf3bae3f2d, 0x7518eaf8e052ad8e}},
+ {{0xa48859c41b7f6c32, 0x0f2d60bcf4383298, 0x1815a929c9b1d1d9, 0x47c3871bbb1755c4}}},
+{{{0x5144539771ec4f48, 0xf805b17dc98c5d6e, 0xf762c11a47c3c66b, 0x00b89b85764699dc}},
+ {{0xfbe65d50c85066b0, 0x62ecc4b0b3a299b0, 0xe53754ea441ae8e0, 0x08fea02ce8d48d5f}},
+ {{0x824ddd7668deead0, 0xc86445204b685d23, 0xb514cfcd5d89d665, 0x473829a74f75d537}}},
+{{{0x82d2da754679c418, 0xe63bd7d8b2618df0, 0x355eef24ac47eb0a, 0x2078684c4833c6b4}},
+ {{0x23d9533aad3902c9, 0x64c2ddceef03588f, 0x15257390cfe12fb4, 0x6c668b4d44e4d390}},
+ {{0x3b48cf217a78820c, 0xf76a0ab281273e97, 0xa96c65a78c8eed7b, 0x7411a6054f8a433f}}},
+{{{0x4d659d32b99dc86d, 0x044cdc75603af115, 0xb34c712cdcc2e488, 0x7c136574fb8134ff}},
+ {{0x579ae53d18b175b4, 0x68713159f392a102, 0x8455ecba1eef35f5, 0x1ec9a872458c398f}},
+ {{0xb8e6a4d400a2509b, 0x9b81d7020bc882b4, 0x57e7cc9bf1957561, 0x3add88a5c7cd6460}}},
+{{{0xab895770b635dcf2, 0x02dfef6cf66c1fbc, 0x85530268beb6d187, 0x249929fccc879e74}},
+ {{0x85c298d459393046, 0x8f7e35985ff659ec, 0x1d2ca22af2f66e3a, 0x61ba1131a406a720}},
+ {{0xa3d0a0f116959029, 0x023b6b6cba7ebd89, 0x7bf15a3e26783307, 0x5620310cbbd8ece7}}},
+{{{0x528993434934d643, 0xb9dbf806a51222f5, 0x8f6d878fc3f41c22, 0x37676a2a4d9d9730}},
+ {{0x6646b5f477e285d6, 0x40e8ff676c8f6193, 0xa6ec7311abb594dd, 0x7ec846f3658cec4d}},
+ {{0x9b5e8f3f1da22ec7, 0x130f1d776c01cd13, 0x214c8fcfa2989fb8, 0x6daaf723399b9dd5}}},
+{{{0x5f3a7562eb3dbe47, 0xf7ea38548ebda0b8, 0x00c3e53145747299, 0x1304e9e71627d551}},
+ {{0x583b04bfacad8ea2, 0x29b743e8148be884, 0x2b1e583b0810c5db, 0x2b5449e58eb3bbaa}},
+ {{0x789814d26adc9cfe, 0x3c1bab3f8b48dd0b, 0xda0fe1fff979c60a, 0x4468de2d7c2dd693}}},
+{{{0x51bb355e9419469e, 0x33e6dc4c23ddc754, 0x93a5b6d6447f9962, 0x6cce7c6ffb44bd63}},
+ {{0x4b9ad8c6f86307ce, 0x21113531435d0c28, 0xd4a866c5657a772c, 0x5da6427e63247352}},
+ {{0x1a94c688deac22ca, 0xb9066ef7bbae1ff8, 0x88ad8c388d59580f, 0x58f29abfe79f2ca8}}},
+{{{0xe90ecfab8de73e68, 0x54036f9f377e76a5, 0xf0495b0bbe015982, 0x577629c4a7f41e36}},
+ {{0x4b5a64bf710ecdf6, 0xb14ce538462c293c, 0x3643d056d50b3ab9, 0x6af93724185b4870}},
+ {{0x3220024509c6a888, 0xd2e036134b558973, 0x83e236233c33289f, 0x701f25bb0caec18f}}},
+{{{0xc3a8b0f8e4616ced, 0xf700660e9e25a87d, 0x61e3061ff4bca59c, 0x2e0c92bfbdc40be9}},
+ {{0x9d18f6d97cbec113, 0x844a06e674bfdbe4, 0x20f5b522ac4e60d6, 0x720a5bc050955e51}},
+ {{0x0c3f09439b805a35, 0xe84e8b376242abfc, 0x691417f35c229346, 0x0e9b9cbb144ef0ec}}},
+{{{0xfbbad48ffb5720ad, 0xee81916bdbf90d0e, 0xd4813152635543bf, 0x221104eb3f337bd8}},
+ {{0x8dee9bd55db1beee, 0xc9c3ab370a723fb9, 0x44a8f1bf1c68d791, 0x366d44191cfd3cde}},
+ {{0x9e3c1743f2bc8c14, 0x2eda26fcb5856c3b, 0xccb82f0e68a7fb97, 0x4167a4e6bc593244}}},
+{{{0x643b9d2876f62700, 0x5d1d9d400e7668eb, 0x1b4b430321fc0684, 0x7938bb7e2255246a}},
+ {{0xc2be2665f8ce8fee, 0xe967ff14e880d62c, 0xf12e6e7e2f364eee, 0x34b33370cb7ed2f6}},
+ {{0xcdc591ee8681d6cc, 0xce02109ced85a753, 0xed7485c158808883, 0x1176fc6e2dfe65e4}}},
+{{{0xb4af6cd05b9c619b, 0x2ddfc9f4b2a58480, 0x3d4fa502ebe94dc4, 0x08fc3a4c677d5f34}},
+ {{0xdb90e28949770eb8, 0x98fbcc2aacf440a3, 0x21354ffeded7879b, 0x1f6a3e54f26906b6}},
+ {{0x60a4c199d30734ea, 0x40c085b631165cd6, 0xe2333e23f7598295, 0x4f2fad0116b900d1}}},
+{{{0x44beb24194ae4e54, 0x5f541c511857ef6c, 0xa61e6b2d368d0498, 0x445484a4972ef7ab}},
+ {{0x962cd91db73bb638, 0xe60577aafc129c08, 0x6f619b39f3b61689, 0x3451995f2944ee81}},
+ {{0x9152fcd09fea7d7c, 0x4a816c94b0935cf6, 0x258e9aaa47285c40, 0x10b89ca6042893b7}}},
+{{{0x3d5947499718289c, 0x12ebf8c524533f26, 0x0262bfcb14c3ef15, 0x20b878d577b7518e}},
+ {{0x753941be5a45f06e, 0xd07caeed6d9c5f65, 0x11776b9c72ff51b6, 0x17d2d1d9ef0d4da9}},
+ {{0x27f2af18073f3e6a, 0xfd3fe519d7521069, 0x22e3b72c3ca60022, 0x72214f63cc65c6a7}}},
+{{{0xb4e37f405307a693, 0xaba714d72f336795, 0xd6fbd0a773761099, 0x5fdf48c58171cbc9}},
+ {{0x1d9db7b9f43b29c9, 0xd605824a4f518f75, 0xf2c072bd312f9dc4, 0x1f24ac855a1545b0}},
+ {{0x24d608328e9505aa, 0x4748c1d10c1420ee, 0xc7ffe45c06fb25a2, 0x00ba739e2ae395e6}}},
+{{{0x592e98de5c8790d6, 0xe5bfb7d345c2a2df, 0x115a3b60f9b49922, 0x03283a3e67ad78f3}},
+ {{0xae4426f5ea88bb26, 0x360679d984973bfb, 0x5c9f030c26694e50, 0x72297de7d518d226}},
+ {{0x48241dc7be0cb939, 0x32f19b4d8b633080, 0xd3dfc90d02289308, 0x05e1296846271945}}},
+{{{0xba82eeb32d9c495a, 0xceefc8fcf12bb97c, 0xb02dabae93b5d1e0, 0x39c00c9c13698d9b}},
+ {{0xadbfbbc8242c4550, 0xbcc80cecd03081d9, 0x843566a6f5c8df92, 0x78cf25d38258ce4c}},
+ {{0x15ae6b8e31489d68, 0xaa851cab9c2bf087, 0xc9a75a97f04efa05, 0x006b52076b3ff832}}},
+{{{0x29e0cfe19d95781c, 0xb681df18966310e2, 0x57df39d370516b39, 0x4d57e3443bc76122}},
+ {{0xf5cb7e16b9ce082d, 0x3407f14c417abc29, 0xd4b36bce2bf4a7ab, 0x7de2e9561a9f75ce}},
+ {{0xde70d4f4b6a55ecb, 0x4801527f5d85db99, 0xdbc9c440d3ee9a81, 0x6b2a90af1a6029ed}}},
+{{{0x6923f4fc9ae61e97, 0x5735281de03f5fd1, 0xa764ae43e6edd12d, 0x5fd8f4e9d12d3e4a}},
+ {{0x77ebf3245bb2d80a, 0xd8301b472fb9079b, 0xc647e6f24cee7333, 0x465812c8276c2109}},
+ {{0x4d43beb22a1062d9, 0x7065fb753831dc16, 0x180d4a7bde2968d7, 0x05b32c2b1cb16790}}},
+{{{0xc8c05eccd24da8fd, 0xa1cf1aac05dfef83, 0xdbbeeff27df9cd61, 0x3b5556a37b471e99}},
+ {{0xf7fca42c7ad58195, 0x3214286e4333f3cc, 0xb6c29d0d340b979d, 0x31771a48567307e1}},
+ {{0x32b0c524e14dd482, 0xedb351541a2ba4b6, 0xa3d16048282b5af3, 0x4fc079d27a7336eb}}},
+{{{0x51c938b089bf2f7f, 0x2497bd6502dfe9a7, 0xffffc09c7880e453, 0x124567cecaf98e92}},
+ {{0xdc348b440c86c50d, 0x1337cbc9cc94e651, 0x6422f74d643e3cb9, 0x241170c2bae3cd08}},
+ {{0x3ff9ab860ac473b4, 0xf0911dee0113e435, 0x4ae75060ebc6c4af, 0x3f8612966c87000d}}},
+{{{0x559a0cc9782a0dde, 0x551dcdb2ea718385, 0x7f62865b31ef238c, 0x504aa7767973613d}},
+ {{0x9c18fcfa36048d13, 0x29159db373899ddd, 0xdc9f350b9f92d0aa, 0x26f57eee878a19d4}},
+ {{0x0cab2cd55687efb1, 0x5180d162247af17b, 0x85c15a344f5a2467, 0x4041943d9dba3069}}},
+{{{0xc3c0eeba43ebcc96, 0x8d749c9c26ea9caf, 0xd9fa95ee1c77ccc6, 0x1420a1d97684340f}},
+ {{0x4b217743a26caadd, 0x47a6b424648ab7ce, 0xcb1d4f7a03fbc9e3, 0x12d931429800d019}},
+ {{0x00c67799d337594f, 0x5e3c5140b23aa47b, 0x44182854e35ff395, 0x1b4f92314359a012}}},
+{{{0x3e5c109d89150951, 0x39cefa912de9696a, 0x20eae43f975f3020, 0x239b572a7f132dae}},
+ {{0x33cf3030a49866b1, 0x251f73d2215f4859, 0xab82aa4051def4f6, 0x5ff191d56f9a23f6}},
+ {{0x819ed433ac2d9068, 0x2883ab795fc98523, 0xef4572805593eb3d, 0x020c526a758f36cb}}},
+{{{0x779834f89ed8dbbc, 0xc8f2aaf9dc7ca46c, 0xa9524cdca3e1b074, 0x02aacc4615313877}},
+ {{0xe931ef59f042cc89, 0x2c589c9d8e124bb6, 0xadc8e18aaec75997, 0x452cfe0a5602c50c}},
+ {{0x86a0f7a0647877df, 0xbbc464270e607c9f, 0xab17ea25f1fb11c9, 0x4cfb7d7b304b877b}}},
+{{{0x72b43d6cb89b75fe, 0x54c694d99c6adc80, 0xb8c3aa373ee34c9f, 0x14b4622b39075364}},
+ {{0xe28699c29789ef12, 0x2b6ecd71df57190d, 0xc343c857ecc970d0, 0x5b1d4cbc434d3ac5}},
+ {{0xb6fb2615cc0a9f26, 0x3a4f0e2bb88dcce5, 0x1301498b3369a705, 0x2f98f71258592dd1}}},
+{{{0x0c94a74cb50f9e56, 0x5b1ff4a98e8e1320, 0x9a2acc2182300f67, 0x3a6ae249d806aaf9}},
+ {{0x2e12ae444f54a701, 0xfcfe3ef0a9cbd7de, 0xcebf890d75835de0, 0x1d8062e9e7614554}},
+ {{0x657ada85a9907c5a, 0x1a0ea8b591b90f62, 0x8d0e1dfbdf34b4e9, 0x298b8ce8aef25ff3}}},
+{{{0x2a927953eff70cb2, 0x4b89c92a79157076, 0x9418457a30a7cf6a, 0x34b8a8404d5ce485}},
+ {{0x837a72ea0a2165de, 0x3fab07b40bcf79f6, 0x521636c77738ae70, 0x6ba6271803a7d7dc}},
+ {{0xc26eecb583693335, 0xd5a813df63b5fefd, 0xa293aa9aa4b22573, 0x71d62bdd465e1c6a}}},
+{{{0x6533cc28d378df80, 0xf6db43790a0fa4b4, 0xe3645ff9f701da5a, 0x74d5f317f3172ba4}},
+ {{0xcd2db5dab1f75ef5, 0xd77f95cf16b065f5, 0x14571fea3f49f085, 0x1c333621262b2b3d}},
+ {{0xa86fe55467d9ca81, 0x398b7c752b298c37, 0xda6d0892e3ac623b, 0x4aebcc4547e9d98c}}},
+{{{0x12f0071b276d01c9, 0xe7b8bac586c48c70, 0x5308129b71d6fba9, 0x5d88fbf95a3db792}},
+ {{0x0b408d9e7354b610, 0x806b32535ba85b6e, 0xdbe63a034a58a207, 0x173bd9ddc9a1df2c}},
+ {{0x2b500f1efe5872df, 0x58d6582ed43918c1, 0xe6ed278ec9673ae0, 0x06e1cd13b19ea319}}},
+{{{0x40d0ad516f166f23, 0x118e32931fab6abe, 0x3fe35e14a04d088e, 0x3080603526e16266}},
+ {{0x472baf629e5b0353, 0x3baa0b90278d0447, 0x0c785f469643bf27, 0x7f3a6a1a8d837b13}},
+ {{0xf7e644395d3d800b, 0x95a8d555c901edf6, 0x68cd7830592c6339, 0x30d0fded2e51307e}}},
+{{{0xe0594d1af21233b3, 0x1bdbe78ef0cc4d9c, 0x6965187f8f499a77, 0x0a9214202c099868}},
+ {{0x9cb4971e68b84750, 0xa09572296664bbcf, 0x5c8de72672fa412b, 0x4615084351c589d9}},
+ {{0xbc9019c0aeb9a02e, 0x55c7110d16034cae, 0x0e6df501659932ec, 0x3bca0d2895ca5dfe}}},
+{{{0x40f031bc3c5d62a4, 0x19fc8b3ecff07a60, 0x98183da2130fb545, 0x5631deddae8f13cd}},
+ {{0x9c688eb69ecc01bf, 0xf0bc83ada644896f, 0xca2d955f5f7a9fe2, 0x4ea8b4038df28241}},
+ {{0x2aed460af1cad202, 0x46305305a48cee83, 0x9121774549f11a5f, 0x24ce0930542ca463}}},
+{{{0x1fe890f5fd06c106, 0xb5c468355d8810f2, 0x827808fe6e8caf3e, 0x41d4e3c28a06d74b}},
+ {{0x3fcfa155fdf30b85, 0xd2f7168e36372ea4, 0xb2e064de6492f844, 0x549928a7324f4280}},
+ {{0xf26e32a763ee1a2e, 0xae91e4b7d25ffdea, 0xbc3bd33bd17f4d69, 0x491b66dec0dcff6a}}},
+{{{0x98f5b13dc7ea32a7, 0xe3d5f8cc7e16db98, 0xac0abf52cbf8d947, 0x08f338d0c85ee4ac}},
+ {{0x75f04a8ed0da64a1, 0xed222caf67e2284b, 0x8234a3791f7b7ba4, 0x4cf6b8b0b7018b67}},
+ {{0xc383a821991a73bd, 0xab27bc01df320c7a, 0xc13d331b84777063, 0x530d4a82eb078a99}}},
+{{{0x004c3630e1f94825, 0x7e2d78268cab535a, 0xc7482323cc84ff8b, 0x65ea753f101770b9}},
+ {{0x6d6973456c9abf9e, 0x257fb2fc4900a880, 0x2bacf412c8cfb850, 0x0db3e7e00cbfbd5b}},
+ {{0x3d66fc3ee2096363, 0x81d62c7f61b5cb6b, 0x0fbe044213443b1a, 0x02a4ec1921e1a1db}}},
+{{{0x5ce6259a3b24b8a2, 0xb8577acc45afa0b8, 0xcccbe6e88ba07037, 0x3d143c51127809bf}},
+ {{0xf5c86162f1cf795f, 0x118c861926ee57f2, 0x172124851c063578, 0x36d12b5dec067fcf}},
+ {{0x126d279179154557, 0xd5e48f5cfc783a0a, 0x36bdb6e8df179bac, 0x2ef517885ba82859}}},
+{{{0x4637974e8c58aedc, 0xb9ef22fbabf041a4, 0xe185d956e980718a, 0x2f1b78fab143a8a6}},
+ {{0x96eebffb305b2f51, 0xd3f938ad889596b8, 0xf0f52dc746d5dd25, 0x57968290bb3a0095}},
+ {{0xf71ab8430a20e101, 0xf393658d24f0ec47, 0xcf7509a86ee2eed1, 0x7dc43e35dc2aa3e1}}},
+{{{0x85966665887dd9c3, 0xc90f9b314bb05355, 0xc6e08df8ef2079b1, 0x7ef72016758cc12f}},
+ {{0x5a782a5c273e9718, 0x3576c6995e4efd94, 0x0f2ed8051f237d3e, 0x044fb81d82d50a99}},
+ {{0xc1df18c5a907e3d9, 0x57b3371dce4c6359, 0xca704534b201bb49, 0x7f79823f9c30dd2e}}},
+{{{0x8334d239a3b513e8, 0xc13670d4b91fa8d8, 0x12b54136f590bd33, 0x0a4e0373d784d9b4}},
+ {{0x6a9c1ff068f587ba, 0x0827894e0050c8de, 0x3cbf99557ded5be7, 0x64a9b0431c06d6f0}},
+ {{0x2eb3d6a15b7d2919, 0xb0b4f6a0d53a8235, 0x7156ce4389a45d47, 0x071a7d0ace18346c}}},
+{{{0xd3072daac887ba0b, 0x01262905bfa562ee, 0xcf543002c0ef768b, 0x2c3bcc7146ea7e9c}},
+ {{0xcc0c355220e14431, 0x0d65950709b15141, 0x9af5621b209d5f36, 0x7c69bcf7617755d3}},
+ {{0x07f0d7eb04e8295f, 0x10db18252f50f37d, 0xe951a9a3171798d7, 0x6f5a9a7322aca51d}}},
+{{{0x8ba1000c2f41c6c5, 0xc49f79c10cfefb9b, 0x4efa47703cc51c9f, 0x494e21a2e147afca}},
+ {{0xe729d4eba3d944be, 0x8d9e09408078af9e, 0x4525567a47869c03, 0x02ab9680ee8d3b24}},
+ {{0xefa48a85dde50d9a, 0x219a224e0fb9a249, 0xfa091f1dd91ef6d9, 0x6b5d76cbea46bb34}}},
+{{{0x8857556cec0cd994, 0x6472dc6f5cd01dba, 0xaf0169148f42b477, 0x0ae333f685277354}},
+ {{0xe0f941171e782522, 0xf1e6ae74036936d3, 0x408b3ea2d0fcc746, 0x16fb869c03dd313e}},
+ {{0x288e199733b60962, 0x24fc72b4d8abe133, 0x4811f7ed0991d03e, 0x3f81e38b8f70d075}}},
+{{{0x7f910fcc7ed9affe, 0x545cb8a12465874b, 0xa8397ed24b0c4704, 0x50510fc104f50993}},
+ {{0x0adb7f355f17c824, 0x74b923c3d74299a4, 0xd57c3e8bcbf8eaf7, 0x0ad3e2d34cdedc3d}},
+ {{0x6f0c0fc5336e249d, 0x745ede19c331cfd9, 0xf2d6fd0009eefe1c, 0x127c158bf0fa1ebe}}},
+{{{0xf6197c422e9879a2, 0xa44addd452ca3647, 0x9b413fc14b4eaccb, 0x354ef87d07ef4f68}},
+ {{0xdea28fc4ae51b974, 0x1d9973d3744dfe96, 0x6240680b873848a8, 0x4ed82479d167df95}},
+ {{0xfee3b52260c5d975, 0x50352efceb41b0b8, 0x8808ac30a9f6653c, 0x302d92d20539236d}}},
+{{{0x7813c1a2bca4283d, 0xed62f091a1863dd9, 0xaec7bcb8c268fa86, 0x10e5d3b76f1cae4c}},
+ {{0x2dbc6fb6e4e0f177, 0x04e1bf29a4bd6a93, 0x5e1966d4787af6e8, 0x0edc5f5eb426d060}},
+ {{0x5453bfd653da8e67, 0xe9dc1eec24a9f641, 0xbf87263b03578a23, 0x45b46c51361cba72}}},
+{{{0xa9402abf314f7fa1, 0xe257f1dc8e8cf450, 0x1dbbd54b23a8be84, 0x2177bfa36dcb713b}},
+ {{0xce9d4ddd8a7fe3e4, 0xab13645676620e30, 0x4b594f7bb30e9958, 0x5c1c0aef321229df}},
+ {{0x37081bbcfa79db8f, 0x6048811ec25f59b3, 0x087a76659c832487, 0x4ae619387d8ab5bb}}},
+{{{0x8ddbf6aa5344a32e, 0x7d88eab4b41b4078, 0x5eb0eb974a130d60, 0x1a00d91b17bf3e03}},
+ {{0x61117e44985bfb83, 0xfce0462a71963136, 0x83ac3448d425904b, 0x75685abe5ba43d64}},
+ {{0x6e960933eb61f2b2, 0x543d0fa8c9ff4952, 0xdf7275107af66569, 0x135529b623b0e6aa}}},
+{{{0x18f0dbd7add1d518, 0x979f7888cfc11f11, 0x8732e1f07114759b, 0x79b5b81a65ca3a01}},
+ {{0xf5c716bce22e83fe, 0xb42beb19e80985c1, 0xec9da63714254aae, 0x5972ea051590a613}},
+ {{0x0fd4ac20dc8f7811, 0x9a9ad294ac4d4fa8, 0xc01b2d64b3360434, 0x4f7e9c95905f3bdb}}},
+{{{0x62674bbc5781302e, 0xd8520f3989addc0f, 0x8c2999ae53fbd9c6, 0x31993ad92e638e4c}},
+ {{0x71c8443d355299fe, 0x8bcd3b1cdbebead7, 0x8092499ef1a49466, 0x1942eec4a144adc8}},
+ {{0x7dac5319ae234992, 0x2c1b3d910cea3e92, 0x553ce494253c1122, 0x2a0a65314ef9ca75}}},
+{{{0x2db7937ff7f927c2, 0xdb741f0617d0a635, 0x5982f3a21155af76, 0x4cf6e218647c2ded}},
+ {{0xcf361acd3c1c793a, 0x2f9ebcac5a35bc3b, 0x60e860e9a8cda6ab, 0x055dc39b6dea1a13}},
+ {{0xb119227cc28d5bb6, 0x07e24ebc774dffab, 0xa83c78cee4a32c89, 0x121a307710aa24b6}}},
+{{{0xe4db5d5e9f034a97, 0xe153fc093034bc2d, 0x460546919551d3b1, 0x333fc76c7a40e52d}},
+ {{0xd659713ec77483c9, 0x88bfe077b82b96af, 0x289e28231097bcd3, 0x527bb94a6ced3a9b}},
+ {{0x563d992a995b482e, 0x3405d07c6e383801, 0x485035de2f64d8e5, 0x6b89069b20a7a9f7}}},
+{{{0x812aa0416270220d, 0x995a89faf9245b4e, 0xffadc4ce5072ef05, 0x23bc2103aa73eb73}},
+ {{0x4082fa8cb5c7db77, 0x068686f8c734c155, 0x29e6c8d9f6e7a57e, 0x0473d308a7639bcf}},
+ {{0xcaee792603589e05, 0x2b4b421246dcc492, 0x02a1ef74e601a94f, 0x102f73bfde04341a}}},
+{{{0xeb18b9ab7f5745c6, 0x023a8aee5787c690, 0xb72712da2df7afa9, 0x36597d25ea5c013d}},
+ {{0xa2b4dae0b5511c9a, 0x7ac860292bffff06, 0x981f375df5504234, 0x3f6bd725da4ea12d}},
+ {{0x734d8d7b106058ac, 0xd940579e6fc6905f, 0x6466f8f99202932d, 0x7b7ecc19da60d6d0}}},
+{{{0x78c2373c695c690d, 0xdd252e660642906e, 0x951d44444ae12bd2, 0x4235ad7601743956}},
+ {{0x6dae4a51a77cfa9b, 0x82263654e7a38650, 0x09bbffcd8f2d82db, 0x03bedc661bf5caba}},
+ {{0x6258cb0d078975f5, 0x492942549189f298, 0xa0cab423e2e36ee4, 0x0e7ce2b0cdf066a1}}},
+{{{0xc494643ac48c85a3, 0xfd361df43c6139ad, 0x09db17dd3ae94d48, 0x666e0a5d8fb4674a}},
+ {{0xfea6fedfd94b70f9, 0xf130c051c1fcba2d, 0x4882d47e7f2fab89, 0x615256138aeceeb5}},
+ {{0x2abbf64e4870cb0d, 0xcd65bcf0aa458b6b, 0x9abe4eba75e8985d, 0x7f0bc810d514dee4}}},
+{{{0xb9006ba426f4136f, 0x8d67369e57e03035, 0xcbc8dfd94f463c28, 0x0d1f8dbcf8eedbf5}},
+ {{0x83ac9dad737213a0, 0x9ff6f8ba2ef72e98, 0x311e2edd43ec6957, 0x1d3a907ddec5ab75}},
+ {{0xba1693313ed081dc, 0x29329fad851b3480, 0x0128013c030321cb, 0x00011b44a31bfde3}}},
+{{{0x3fdfa06c3fc66c0c, 0x5d40e38e4dd60dd2, 0x7ae38b38268e4d71, 0x3ac48d916e8357e1}},
+ {{0x16561f696a0aa75c, 0xc1bf725c5852bd6a, 0x11a8dd7f9a7966ad, 0x63d988a2d2851026}},
+ {{0x00120753afbd232e, 0xe92bceb8fdd8f683, 0xf81669b384e72b91, 0x33fad52b2368a066}}},
+{{{0x540649c6c5e41e16, 0x0af86430333f7735, 0xb2acfcd2f305e746, 0x16c0f429a256dca7}},
+ {{0x8d2cc8d0c422cfe8, 0x072b4f7b05a13acb, 0xa3feb6e6ecf6a56f, 0x3cc355ccb90a71e2}},
+ {{0xe9b69443903e9131, 0xb8a494cb7a5637ce, 0xc87cd1a4baba9244, 0x631eaf426bae7568}}},
+{{{0xb3e90410da66fe9f, 0x85dd4b526c16e5a6, 0xbc3d97611ef9bf83, 0x5599648b1ea919b5}},
+ {{0x47d975b9a3700de8, 0x7280c5fbe2f80552, 0x53658f2732e45de1, 0x431f2c7f665f80b5}},
+ {{0xd6026344858f7b19, 0x14ab352fa1ea514a, 0x8900441a2090a9d7, 0x7b04715f91253b26}}},
+{{{0x83edbd28acf6ae43, 0x86357c8b7d5c7ab4, 0xc0404769b7eb2c44, 0x59b37bf5c2f6583f}},
+ {{0xb376c280c4e6bac6, 0x970ed3dd6d1d9b0b, 0xb09a9558450bf944, 0x48d0acfa57cde223}},
+ {{0xb60f26e47dabe671, 0xf1d1a197622f3a37, 0x4208ce7ee9960394, 0x16234191336d3bdb}}},
+{{{0xb9e499def6267ff6, 0x7772ca7b742c0843, 0x23a0153fe9a4f2b1, 0x2cdfdfecd5d05006}},
+ {{0xdd499cd61ff38640, 0x29cd9bc3063625a0, 0x51e2d8023dd73dc3, 0x4a25707a203b9231}},
+ {{0x2ab7668a53f6ed6a, 0x304242581dd170a1, 0x4000144c3ae20161, 0x5721896d248e49fc}}},
+{{{0x0b6e5517fd181bae, 0x9022629f2bb963b4, 0x5509bce932064625, 0x578edd74f63c13da}},
+ {{0x285d5091a1d0da4e, 0x4baa6fa7b5fe3e08, 0x63e5177ce19393b3, 0x03c935afc4b030fd}},
+ {{0x997276c6492b0c3d, 0x47ccc2c4dfe205fc, 0xdcd29b84dd623a3c, 0x3ec2ab590288c7a2}}},
+{{{0xa1a0d27be4d87bb9, 0xa98b4deb61391aed, 0x99a0ddd073cb9b83, 0x2dd5c25a200fcace}},
+ {{0xa7213a09ae32d1cb, 0x0f2b87df40f5c2d5, 0x0baea4c6e81eab29, 0x0e1bf66c6adbac5e}},
+ {{0xe2abd5e9792c887e, 0x1a020018cb926d5d, 0xbfba69cdbaae5f1e, 0x730548b35ae88f5f}}},
+{{{0xc43551a3cba8b8ee, 0x65a26f1db2115f16, 0x760f4f52ab8c3850, 0x3043443b411db8ca}},
+ {{0x805b094ba1d6e334, 0xbf3ef17709353f19, 0x423f06cb0622702b, 0x585a2277d87845dd}},
+ {{0xa18a5f8233d48962, 0x6698c4b5ec78257f, 0xa78e6fa5373e41ff, 0x7656278950ef981f}}},
+{{{0x38c3cf59d51fc8c0, 0x9bedd2fd0506b6f2, 0x26bf109fab570e8f, 0x3f4160a8c1b846a6}},
+ {{0xe17073a3ea86cf9d, 0x3a8cfbb707155fdc, 0x4853e7fc31838a8e, 0x28bbf484b613f616}},
+ {{0xf2612f5c6f136c7c, 0xafead107f6dd11be, 0x527e9ad213de6f33, 0x1e79cb358188f75d}}},
+{{{0x013436c3eef7e3f1, 0x828b6a7ffe9e10f8, 0x7ff908e5bcf9defc, 0x65d7951b3a3b3831}},
+ {{0x77e953d8f5e08181, 0x84a50c44299dded9, 0xdc6c2d0c864525e5, 0x478ab52d39d1f2f4}},
+ {{0x66a6a4d39252d159, 0xe5dde1bc871ac807, 0xb82c6b40a6c1c96f, 0x16d87a411a212214}}},
+{{{0xb3bd7e5a42066215, 0x879be3cd0c5a24c1, 0x57c05db1d6f994b7, 0x28f87c8165f38ca6}},
+ {{0xfba4d5e2d54e0583, 0xe21fafd72ebd99fa, 0x497ac2736ee9778f, 0x1f990b577a5a6dde}},
+ {{0xa3344ead1be8f7d6, 0x7d1e50ebacea798f, 0x77c6569e520de052, 0x45882fe1534d6d3e}}},
+{{{0x6669345d757983d6, 0x62b6ed1117aa11a6, 0x7ddd1857985e128f, 0x688fe5b8f626f6dd}},
+ {{0xd8ac9929943c6fe4, 0xb5f9f161a38392a2, 0x2699db13bec89af3, 0x7dcf843ce405f074}},
+ {{0x6c90d6484a4732c0, 0xd52143fdca563299, 0xb3be28c3915dc6e1, 0x6739687e7327191b}}},
+{{{0xef782014385675a6, 0xa2649f30aafda9e8, 0x4cd1eb505cdfa8cb, 0x46115aba1d4dc0b3}},
+ {{0xa66dcc9dc80c1ac0, 0x97a05cf41b38a436, 0xa7ebf3be95dbd7c6, 0x7da0b8f68d7e7dab}},
+ {{0xd40f1953c3b5da76, 0x1dac6f7321119e9b, 0x03cc6021feb25960, 0x5a5f887e83674b4b}}},
+{{{0x8f6301cf70a13d11, 0xcfceb815350dd0c4, 0xf70297d4a4bca47e, 0x3669b656e44d1434}},
+ {{0x9e9628d3a0a643b9, 0xb5c3cb00e6c32064, 0x9b5302897c2dec32, 0x43e37ae2d5d1c70c}},
+ {{0x387e3f06eda6e133, 0x67301d5199a13ac0, 0xbd5ad8f836263811, 0x6a21e6cd4fd5e9be}}},
+{{{0xf1c6170a3046e65f, 0x58712a2a00d23524, 0x69dbbd3c8c82b755, 0x586bf9f1a195ff57}},
+ {{0xef4129126699b2e3, 0x71d30847708d1301, 0x325432d01182b0bd, 0x45371b07001e8b36}},
+ {{0xa6db088d5ef8790b, 0x5278f0dc610937e5, 0xac0349d261a16eb8, 0x0eafb03790e52179}}},
+{{{0x960555c13748042f, 0x219a41e6820baa11, 0x1c81f73873486d0c, 0x309acc675a02c661}},
+ {{0x5140805e0f75ae1d, 0xec02fbe32662cc30, 0x2cebdf1eea92396d, 0x44ae3344c5435bb3}},
+ {{0x9cf289b9bba543ee, 0xf3760e9d5ac97142, 0x1d82e5c64f9360aa, 0x62d5221b7f94678f}}},
+{{{0x524c299c18d0936d, 0xc86bb56c8a0c1a0c, 0xa375052edb4a8631, 0x5c0efde4bc754562}},
+ {{0x7585d4263af77a3c, 0xdfae7b11fee9144d, 0xa506708059f7193d, 0x14f29a5383922037}},
+ {{0xdf717edc25b2d7f5, 0x21f970db99b53040, 0xda9234b7c3ed4c62, 0x5e72365c7bee093e}}},
+{{{0x575bfc074571217f, 0x3779675d0694d95b, 0x9a0a37bbf4191e33, 0x77f1104c47b4eabc}},
+ {{0x7d9339062f08b33e, 0x5b9659e5df9f32be, 0xacff3dad1f9ebdfd, 0x70b20555cb7349b7}},
+ {{0xbe5113c555112c4c, 0x6688423a9a881fcd, 0x446677855e503b47, 0x0e34398f4a06404a}}},
+{{{0xb67d22d93ecebde8, 0x09b3e84127822f07, 0x743fa61fb05b6d8d, 0x5e5405368a362372}},
+ {{0x18930b093e4b1928, 0x7de3e10e73f3f640, 0xf43217da73395d6f, 0x6f8aded6ca379c3e}},
+ {{0xe340123dfdb7b29a, 0x487b97e1a21ab291, 0xf9967d02fde6949e, 0x780de72ec8d3de97}}},
+{{{0x0ae28545089ae7bc, 0x388ddecf1c7f4d06, 0x38ac15510a4811b8, 0x0eb28bf671928ce4}},
+ {{0x671feaf300f42772, 0x8f72eb2a2a8c41aa, 0x29a17fd797373292, 0x1defc6ad32b587a6}},
+ {{0xaf5bbe1aef5195a7, 0x148c1277917b15ed, 0x2991f7fb7ae5da2e, 0x467d201bf8dd2867}}},
+{{{0x95fe919a74ef4fad, 0x3a827becf6a308a2, 0x964e01d309a47b01, 0x71c43c4f5ba3c797}},
+ {{0xbc1ef4bd567ae7a9, 0x3f624cb2d64498bd, 0xe41064d22c1f4ec8, 0x2ef9c5a5ba384001}},
+ {{0xb6fd6df6fa9e74cd, 0xf18278bce4af267a, 0x8255b3d0f1ef990e, 0x5a758ca390c5f293}}},
+{{{0xa2b72710d9462495, 0x3aa8c6d2d57d5003, 0xe3d400bfa0b487ca, 0x2dbae244b3eb72ec}},
+ {{0x8ce0918b1d61dc94, 0x8ded36469a813066, 0xd4e6a829afe8aad3, 0x0a738027f639d43f}},
+ {{0x980f4a2f57ffe1cc, 0x00670d0de1839843, 0x105c3f4a49fb15fd, 0x2698ca635126a69c}}},
+{{{0xe765318832b0ba78, 0x381831f7925cff8b, 0x08a81b91a0291fcc, 0x1fb43dcc49caeb07}},
+ {{0x2e3d702f5e3dd90e, 0x9e3f0918e4d25386, 0x5e773ef6024da96a, 0x3c004b0c4afa3332}},
+ {{0x9aa946ac06f4b82b, 0x1ca284a5a806c4f3, 0x3ed3265fc6cd4787, 0x6b43fd01cd1fd217}}},
+{{{0xc7a75d4b4697c544, 0x15fdf848df0fffbf, 0x2868b9ebaa46785a, 0x5a68d7105b52f714}},
+ {{0xb5c742583e760ef3, 0x75dc52b9ee0ab990, 0xbf1427c2072b923f, 0x73420b2d6ff0d9f0}},
+ {{0xaf2cf6cb9e851e06, 0x8f593913c62238c4, 0xda8ab89699fbf373, 0x3db5632fea34bc9e}}},
+{{{0xf46eee2bf75dd9d8, 0x0d17b1f6396759a5, 0x1bf2d131499e7273, 0x04321adf49d75f13}},
+ {{0x2e4990b1829825d5, 0xedeaeb873e9a8991, 0xeef03d394c704af8, 0x59197ea495df2b0e}},
+ {{0x04e16019e4e55aae, 0xe77b437a7e2f92e9, 0xc7ce2dc16f159aa4, 0x45eafdc1f4d70cc0}}},
+{{{0x698401858045d72b, 0x4c22faa2cf2f0651, 0x941a36656b222dc6, 0x5a5eebc80362dade}},
+ {{0xb60e4624cfccb1ed, 0x59dbc292bd5c0395, 0x31a09d1ddc0481c9, 0x3f73ceea5d56d940}},
+ {{0xb7a7bfd10a4e8dc6, 0xbe57007e44c9b339, 0x60c1207f1557aefa, 0x26058891266218db}}},
+{{{0x59f704a68360ff04, 0xc3d93fde7661e6f4, 0x831b2a7312873551, 0x54ad0c2e4e615d57}},
+ {{0x4c818e3cc676e542, 0x5e422c9303ceccad, 0xec07cccab4129f08, 0x0dedfa10b24443b8}},
+ {{0xee3b67d5b82b522a, 0x36f163469fa5c1eb, 0xa5b4d2f26ec19fd3, 0x62ecb2baa77a9408}}},
+{{{0xe5ed795261152b3d, 0x4962357d0eddd7d1, 0x7482c8d0b96b4c71, 0x2e59f919a966d8be}},
+ {{0x92072836afb62874, 0x5fcd5e8579e104a5, 0x5aad01adc630a14a, 0x61913d5075663f98}},
+ {{0x0dc62d361a3231da, 0xfa47583294200270, 0x02d801513f9594ce, 0x3ddbc2a131c05d5c}}},
+{{{0x9adc0ff9ce5ec54b, 0x039c2a6b8c2f130d, 0x028007c7f0f89515, 0x78968314ac04b36b}},
+ {{0xf3aa57a22796bb14, 0x883abab79b07da21, 0xe54be21831a0391c, 0x5ee7fb38d83205f9}},
+ {{0x538dfdcb41446a8e, 0xa5acfda9434937f9, 0x46af908d263c8c78, 0x61d0633c9bca0d09}}},
+{{{0x63744935ffdb2566, 0xc5bd6b89780b68bb, 0x6f1b3280553eec03, 0x6e965fd847aed7f5}},
+ {{0xada328bcf8fc73df, 0xee84695da6f037fc, 0x637fb4db38c2a909, 0x5b23ac2df8067bdc}},
+ {{0x9ad2b953ee80527b, 0xe88f19aafade6d8d, 0x0e711704150e82cf, 0x79b9bbb9dd95dedc}}},
+{{{0xebb355406a3126c2, 0xd26383a868c8c393, 0x6c0c6429e5b97a82, 0x5065f158c9fd2147}},
+ {{0xd1997dae8e9f7374, 0xa032a2f8cfbb0816, 0xcd6cba126d445f0a, 0x1ba811460accb834}},
+ {{0x708169fb0c429954, 0xe14600acd76ecf67, 0x2eaab98a70e645ba, 0x3981f39e58a4faf2}}},
+{{{0x18fb8a7559230a93, 0x1d168f6960e6f45d, 0x3a85a94514a93cb5, 0x38dc083705acd0fd}},
+ {{0xc845dfa56de66fde, 0xe152a5002c40483a, 0xe9d2e163c7b4f632, 0x30f4452edcbc1b65}},
+ {{0x856d2782c5759740, 0xfa134569f99cbecc, 0x8844fc73c0ea4e71, 0x632d9a1a593f2469}}},
+{{{0xf6bb6b15b807cba6, 0x1823c7dfbc54f0d7, 0xbb1d97036e29670b, 0x0b24f48847ed4a57}},
+ {{0xbf09fd11ed0c84a7, 0x63f071810d9f693a, 0x21908c2d57cf8779, 0x3a5a7df28af64ba2}},
+ {{0xdcdad4be511beac7, 0xa4538075ed26ccf2, 0xe19cff9f005f9a65, 0x34fcf74475481f63}}},
+{{{0xc197e04c789767ca, 0xb8714dcb38d9467d, 0x55de888283f95fa8, 0x3d3bdc164dfa63f7}},
+ {{0xa5bb1dab78cfaa98, 0x5ceda267190b72f2, 0x9309c9110a92608e, 0x0119a3042fb374b0}},
+ {{0x67a2d89ce8c2177d, 0x669da5f66895d0c1, 0xf56598e5b282a2b0, 0x56c088f1ede20a73}}},
+{{{0x336d3d1110a86e17, 0xd7f388320b75b2fa, 0xf915337625072988, 0x09674c6b99108b87}},
+ {{0x581b5fac24f38f02, 0xa90be9febae30cbd, 0x9a2169028acf92f0, 0x038b7ea48359038f}},
+ {{0x9f4ef82199316ff8, 0x2f49d282eaa78d4f, 0x0971a5ab5aef3174, 0x6e5e31025969eb65}}},
+{{{0xb16c62f587e593fb, 0x4999eddeca5d3e71, 0xb491c1e014cc3e6d, 0x08f5114789a8dba8}},
+ {{0x3304fb0e63066222, 0xfb35068987acba3f, 0xbd1924778c1061a3, 0x3058ad43d1838620}},
+ {{0x323c0ffde57663d0, 0x05c3df38a22ea610, 0xbdc78abdac994f9a, 0x26549fa4efe3dc99}}},
+{{{0x741d5a461e6bf9d6, 0x2305b3fc7777a581, 0xd45574a26474d3d9, 0x1926e1dc6401e0ff}},
+ {{0xdb468549af3f666e, 0xd77fcf04f14a0ea5, 0x3df23ff7a4ba0c47, 0x3a10dfe132ce3c85}},
+ {{0xe07f4e8aea17cea0, 0x2fd515463a1fc1fd, 0x175322fd31f2c0f1, 0x1fa1d01d861e5d15}}},
+{{{0xcc8055947d599832, 0x1e4656da37f15520, 0x99f6f7744e059320, 0x773563bc6a75cf33}},
+ {{0x38dcac00d1df94ab, 0x2e712bddd1080de9, 0x7f13e93efdd5e262, 0x73fced18ee9a01e5}},
+ {{0x06b1e90863139cb3, 0xa493da67c5a03ecd, 0x8d77cec8ad638932, 0x1f426b701b864f44}}},
+{{{0xefc9264c41911c01, 0xf1a3b7b817a22c25, 0x5875da6bf30f1447, 0x4e1af5271d31b090}},
+ {{0xf17e35c891a12552, 0xb76b8153575e9c76, 0xfa83406f0d9b723e, 0x0b76bb1b3fa7e438}},
+ {{0x08b8c1f97f92939b, 0xbe6771cbd444ab6e, 0x22e5646399bb8017, 0x7b6dd61eb772a955}}},
+{{{0xb7adc1e850f33d92, 0x7998fa4f608cd5cf, 0xad962dbd8dfc5bdb, 0x703e9bceaf1d2f4f}},
+ {{0x5730abf9ab01d2c7, 0x16fb76dc40143b18, 0x866cbe65a0cbb281, 0x53fa9b659bff6afe}},
+ {{0x6c14c8e994885455, 0x843a5d6665aed4e5, 0x181bb73ebcd65af1, 0x398d93e5c4c61f50}}},
+{{{0x1c4bd16733e248f3, 0xbd9e128715bf0a5f, 0xd43f8cf0a10b0376, 0x53b09b5ddf191b13}},
+ {{0xc3877c60d2e7e3f2, 0x3b34aaa030828bb1, 0x283e26e7739ef138, 0x699c9c9002c30577}},
+ {{0xf306a7235946f1cc, 0x921718b5cce5d97d, 0x28cdd24781b4e975, 0x51caf30c6fcdd907}}},
+{{{0xa60ba7427674e00a, 0x630e8570a17a7bf3, 0x3758563dcf3324cc, 0x5504aa292383fdaa}},
+ {{0x737af99a18ac54c7, 0x903378dcc51cb30f, 0x2b89bc334ce10cc7, 0x12ae29c189f8e99a}},
+ {{0xa99ec0cb1f0d01cf, 0x0dd1efcc3a34f7ae, 0x55ca7521d09c4e22, 0x5fd14fe958eba5ea}}},
+{{{0xb5dc2ddf2845ab2c, 0x069491b10a7fe993, 0x4daaf3d64002e346, 0x093ff26e586474d1}},
+ {{0x3c42fe5ebf93cb8e, 0xbedfa85136d4565f, 0xe0f0859e884220e8, 0x7dd73f960725d128}},
+ {{0xb10d24fe68059829, 0x75730672dbaf23e5, 0x1367253ab457ac29, 0x2f59bcbc86b470a4}}},
+{{{0x83847d429917135f, 0xad1b911f567d03d7, 0x7e7748d9be77aad1, 0x5458b42e2e51af4a}},
+ {{0x7041d560b691c301, 0x85201b3fadd7e71e, 0x16c2e16311335585, 0x2aa55e3d010828b1}},
+ {{0xed5192e60c07444f, 0x42c54e2d74421d10, 0x352b4c82fdb5c864, 0x13e9004a8a768664}}},
+{{{0x739d8845832fcedb, 0xfa38d6c9ae6bf863, 0x32bc0dcab74ffef7, 0x73937e8814bce45e}},
+ {{0xbb2e00c9193b877f, 0xece3a890e0dc506b, 0xecf3b7c036de649f, 0x5f46040898de9e1a}},
+ {{0xb9037116297bf48d, 0xa9d13b22d4f06834, 0xe19715574696bdc6, 0x2cf8a4e891d5e835}}},
+{{{0x6d93fd8707110f67, 0xdd4c09d37c38b549, 0x7cb16a4cc2736a86, 0x2049bd6e58252a09}},
+ {{0x2cb5487e17d06ba2, 0x24d2381c3950196b, 0xd7659c8185978a30, 0x7a6f7f2891d6a4f6}},
+ {{0x7d09fd8d6a9aef49, 0xf0ee60be5b3db90b, 0x4c21b52c519ebfd4, 0x6011aadfc545941d}}},
+{{{0x5f67926dcf95f83c, 0x7c7e856171289071, 0xd6a1e7f3998f7a5b, 0x6fc5cc1b0b62f9e0}},
+ {{0x63ded0c802cbf890, 0xfbd098ca0dff6aaa, 0x624d0afdb9b6ed99, 0x69ce18b779340b1e}},
+ {{0xd1ef5528b29879cb, 0xdd1aae3cd47e9092, 0x127e0442189f2352, 0x15596b3ae57101f1}}},
+{{{0x462739d23f9179a2, 0xff83123197d6ddcf, 0x1307deb553f2148a, 0x0d2237687b5f4dda}},
+ {{0x09ff31167e5124ca, 0x0be4158bd9c745df, 0x292b7d227ef556e5, 0x3aa4e241afb6d138}},
+ {{0x2cc138bf2a3305f5, 0x48583f8fa2e926c3, 0x083ab1a25549d2eb, 0x32fcaa6e4687a36c}}},
+{{{0x7bc56e8dc57d9af5, 0x3e0bd2ed9df0bdf2, 0xaac014de22efe4a3, 0x4627e9cefebd6a5c}},
+ {{0x3207a4732787ccdf, 0x17e31908f213e3f8, 0xd5b2ecd7f60d964e, 0x746f6336c2600be9}},
+ {{0x3f4af345ab6c971c, 0xe288eb729943731f, 0x33596a8a0344186d, 0x7b4917007ed66293}}},
+{{{0x2d85fb5cab84b064, 0x497810d289f3bc14, 0x476adc447b15ce0c, 0x122ba376f844fd7b}},
+ {{0x54341b28dd53a2dd, 0xaa17905bdf42fc3f, 0x0ff592d94dd2f8f4, 0x1d03620fe08cd37d}},
+ {{0xc20232cda2b4e554, 0x9ed0fd42115d187f, 0x2eabb4be7dd479d9, 0x02c70bf52b68ec4c}}},
+{{{0xa287ec4b5d0b2fbb, 0x415c5790074882ca, 0xe044a61ec1d0815c, 0x26334f0a409ef5e0}},
+ {{0xace532bf458d72e1, 0x5be768e07cb73cb5, 0x56cf7d94ee8bbde7, 0x6b0697e3feb43a03}},
+ {{0xb6c8f04adf62a3c0, 0x3ef000ef076da45d, 0x9c9cb95849f0d2a9, 0x1cc37f43441b2fae}}},
+{{{0x508f565a5cc7324f, 0xd061c4c0e506a922, 0xfb18abdb5c45ac19, 0x6c6809c10380314a}},
+ {{0xd76656f1c9ceaeb9, 0x1c5b15f818e5656a, 0x26e72832844c2334, 0x3a346f772f196838}},
+ {{0xd2d55112e2da6ac8, 0xe9bd0331b1e851ed, 0x960746dd8ec67262, 0x05911b9f6ef7c5d0}}},
+{{{0xc1339983f5df0ebb, 0xc0f3758f512c4cac, 0x2cf1130a0bb398e1, 0x6b3cecf9aa270c62}},
+ {{0x5349acf3512eeaef, 0x20c141d31cc1cb49, 0x24180c07a99a688d, 0x555ef9d1c64b2d17}},
+ {{0x36a770ba3b73bd08, 0x624aef08a3afbf0c, 0x5737ff98b40946f2, 0x675f4de13381749d}}},
+{{{0x0e2c52036b1782fc, 0x64816c816cad83b4, 0xd0dcbdd96964073e, 0x13d99df70164c520}},
+ {{0xa12ff6d93bdab31d, 0x0725d80f9d652dfe, 0x019c4ff39abe9487, 0x60f450b882cd3c43}},
+ {{0x014b5ec321e5c0ca, 0x4fcb69c9d719bfa2, 0x4e5f1c18750023a0, 0x1c06de9e55edac80}}},
+{{{0x990f7ad6a33ec4e2, 0x6608f938be2ee08e, 0x9ca143c563284515, 0x4cf38a1fec2db60d}},
+ {{0xffd52b40ff6d69aa, 0x34530b18dc4049bb, 0x5e4a5c2fa34d9897, 0x78096f8e7d32ba2d}},
+ {{0xa0aaaa650dfa5ce7, 0xf9c49e2a48b5478c, 0x4f09cc7d7003725b, 0x373cad3a26091abe}}},
+{{{0xb294634d82c9f57c, 0x1fcbfde124934536, 0x9e9c4db3418cdb5a, 0x0040f3d9454419fc}},
+ {{0xf1bea8fb89ddbbad, 0x3bcb2cbc61aeaecb, 0x8f58a7bb1f9b8d9d, 0x21547eda5112a686}},
+ {{0xdefde939fd5986d3, 0xf4272c89510a380c, 0xb72ba407bb3119b9, 0x63550a334a254df4}}},
+{{{0x6507d6edb569cf37, 0x178429b00ca52ee1, 0xea7c0090eb6bd65d, 0x3eea62c7daf78f51}},
+ {{0x9bba584572547b49, 0xf305c6fae2c408e0, 0x60e8fa69c734f18d, 0x39a92bafaa7d767a}},
+ {{0x9d24c713e693274e, 0x5f63857768dbd375, 0x70525560eb8ab39a, 0x68436a0665c9c4cd}}},
+{{{0xbc0235e8202f3f27, 0xc75c00e264f975b0, 0x91a4e9d5a38c2416, 0x17b6e7f68ab789f9}},
+ {{0x1e56d317e820107c, 0xc5266844840ae965, 0xc1e0a1c6320ffc7a, 0x5373669c91611472}},
+ {{0x5d2814ab9a0e5257, 0x908f2084c9cab3fc, 0xafcaf5885b2d1eca, 0x1cb4b5a678f87d11}}},
+{{{0xb664c06b394afc6c, 0x0c88de2498da5fb1, 0x4f8d03164bcad834, 0x330bca78de7434a2}},
+ {{0x6b74aa62a2a007e7, 0xf311e0b0f071c7b1, 0x5707e438000be223, 0x2dc0fd2d82ef6eac}},
+ {{0x982eff841119744e, 0xf9695e962b074724, 0xc58ac14fbfc953fb, 0x3c31be1b369f1cf5}}},
+{{{0xb0f4864d08948aee, 0x07dc19ee91ba1c6f, 0x7975cdaea6aca158, 0x330b61134262d4bb}},
+ {{0xc168bc93f9cb4272, 0xaeb8711fc7cedb98, 0x7f0e52aa34ac8d7a, 0x41cec1097e7d55bb}},
+ {{0xf79619d7a26d808a, 0xbb1fd49e1d9e156d, 0x73d7c36cdba1df27, 0x26b44cd91f28777d}}},
+{{{0x51f048478f387475, 0xb25dbcf49cbecb3c, 0x9aab1244d99f2055, 0x2c709e6c1c10a5d6}},
+ {{0xe1b7f29362730383, 0x4b5279ffebca8a2c, 0xdafc778abfd41314, 0x7deb10149c72610f}},
+ {{0xcb62af6a8766ee7a, 0x66cbec045553cd0e, 0x588001380f0be4b5, 0x08e68e9ff62ce2ea}}},
+{{{0x34ad500a4bc130ad, 0x8d38db493d0bd49c, 0xa25c3d98500a89be, 0x2f1f3f87eeba3b09}},
+ {{0x2f2d09d50ab8f2f9, 0xacb9218dc55923df, 0x4a8f342673766cb9, 0x4cb13bd738f719f5}},
+ {{0xf7848c75e515b64a, 0xa59501badb4a9038, 0xc20d313f3f751b50, 0x19a1e353c0ae2ee8}}},
+{{{0x7d1c7560bafa05c3, 0xb3e1a0a0c6e55e61, 0xe3529718c0d66473, 0x41546b11c20c3486}},
+ {{0xb42172cdd596bdbd, 0x93e0454398eefc40, 0x9fb15347b44109b5, 0x736bd3990266ae34}},
+ {{0x85532d509334b3b4, 0x46fd114b60816573, 0xcc5f5f30425c8375, 0x412295a2b87fab5c}}},
+{{{0x19c99b88f57ed6e9, 0x5393cb266df8c825, 0x5cee3213b30ad273, 0x14e153ebb52d2e34}},
+ {{0x2e655261e293eac6, 0x845a92032133acdb, 0x460975cb7900996b, 0x0760bb8d195add80}},
+ {{0x413e1a17cde6818a, 0x57156da9ed69a084, 0x2cbf268f46caccb1, 0x6b34be9bc33ac5f2}}},
+{{{0xf3df2f643a78c0b2, 0x4c3e971ef22e027c, 0xec7d1c5e49c1b5a3, 0x2012c18f0922dd2d}},
+ {{0x11fc69656571f2d3, 0xc6c9e845530e737a, 0xe33ae7a2d4fe5035, 0x01b9c7b62e6dd30b}},
+ {{0x880b55e55ac89d29, 0x1483241f45a0a763, 0x3d36efdfc2e76c1f, 0x08af5b784e4bade8}}},
+{{{0x283499dc881f2533, 0x9d0525da779323b6, 0x897addfb673441f4, 0x32b79d71163a168d}},
+ {{0xe27314d289cc2c4b, 0x4be4bd11a287178d, 0x18d528d6fa3364ce, 0x6423c1d5afd9826e}},
+ {{0xcc85f8d9edfcb36a, 0x22bcc28f3746e5f9, 0xe49de338f9e5d3cd, 0x480a5efbc13e2dcc}}},
+{{{0x0b51e70b01622071, 0x06b505cf8b1dafc5, 0x2c6bb061ef5aabcd, 0x47aa27600cb7bf31}},
+ {{0xb6614ce442ce221f, 0x6e199dcc4c053928, 0x663fb4a4dc1cbe03, 0x24b31d47691c8e06}},
+ {{0x2a541eedc015f8c3, 0x11a4fe7e7c693f7c, 0xf0af66134ea278d6, 0x545b585d14dda094}}},
+{{{0x67bf275ea0d43a0f, 0xade68e34089beebe, 0x4289134cd479e72e, 0x0f62f9c332ba5454}},
+ {{0x6204e4d0e3b321e1, 0x3baa637a28ff1e95, 0x0b0ccffd5b99bd9e, 0x4d22dc3e64c8d071}},
+ {{0xfcb46589d63b5f39, 0x5cae6a3f57cbcf61, 0xfebac2d2953afa05, 0x1c0fa01a36371436}}},
+{{{0xd2c604b622943dff, 0xbc8cbece44cfb3a0, 0x5d254ff397808678, 0x0fa3614f3b1ca6bf}},
+ {{0x69082b0e8c936a50, 0xf9c9a035c1dac5b6, 0x6fb73e54c4dfb634, 0x4005419b1d2bc140}},
+ {{0xa003febdb9be82f0, 0x2089c1af3a44ac90, 0xf8499f911954fa8e, 0x1fba218aef40ab42}}},
+{{{0xab549448fac8f53e, 0x81f6e89a7ba63741, 0x74fd6c7d6c2b5e01, 0x392e3acaa8c86e42}},
+ {{0x4f3e57043e7b0194, 0xa81d3eee08daaf7f, 0xc839c6ab99dcdef1, 0x6c535d13ff7761d5}},
+ {{0x4cbd34e93e8a35af, 0x2e0781445887e816, 0x19319c76f29ab0ab, 0x25e17fe4d50ac13b}}},
+{{{0x0a289bd71e04f676, 0x208e1c52d6420f95, 0x5186d8b034691fab, 0x255751442a9fb351}},
+ {{0x915f7ff576f121a7, 0xc34a32272fcd87e3, 0xccba2fde4d1be526, 0x6bba828f8969899b}},
+ {{0xe2d1bc6690fe3901, 0x4cb54a18a0997ad5, 0x971d6914af8460d4, 0x559d504f7f6b7be4}}},
+{{{0xa7738378b3eb54d5, 0x1d69d366a5553c7c, 0x0a26cf62f92800ba, 0x01ab12d5807e3217}},
+ {{0x9c4891e7f6d266fd, 0x0744a19b0307781b, 0x88388f1d6061e23b, 0x123ea6a3354bd50e}},
+ {{0x118d189041e32d96, 0xb9ede3c2d8315848, 0x1eab4271d83245d9, 0x4a3961e2c918a154}}},
+{{{0x71dc3be0f8e6bba0, 0xd6cef8347effe30a, 0xa992425fe13a476a, 0x2cd6bce3fb1db763}},
+ {{0x0327d644f3233f1e, 0x499a260e34fcf016, 0x83b5a716f2dab979, 0x68aceead9bd4111f}},
+ {{0x38b4c90ef3d7c210, 0x308e6e24b7ad040c, 0x3860d9f1b7e73e23, 0x595760d5b508f597}}},
+{{{0x6129bfe104aa6397, 0x8f960008a4a7fccb, 0x3f8bc0897d909458, 0x709fa43edcb291a9}},
+ {{0x882acbebfd022790, 0x89af3305c4115760, 0x65f492e37d3473f4, 0x2cb2c5df54515a2b}},
+ {{0xeb0a5d8c63fd2aca, 0xd22bc1662e694eff, 0x2723f36ef8cbb03a, 0x70f029ecf0c8131f}}},
+{{{0x461307b32eed3e33, 0xae042f33a45581e7, 0xc94449d3195f0366, 0x0b7d5d8a6c314858}},
+ {{0x2a6aafaa5e10b0b9, 0x78f0a370ef041aa9, 0x773efb77aa3ad61f, 0x44eca5a2a74bd9e1}},
+ {{0x25d448327b95d543, 0x70d38300a3340f1d, 0xde1c531c60e1c52b, 0x272224512c7de9e4}}},
+{{{0x1abc92af49c5342e, 0xffeed811b2e6fad0, 0xefa28c8dfcc84e29, 0x11b5df18a44cc543}},
+ {{0xbf7bbb8a42a975fc, 0x8c5c397796ada358, 0xe27fc76fcdedaa48, 0x19735fd7f6bc20a6}},
+ {{0xe3ab90d042c84266, 0xeb848e0f7f19547e, 0x2503a1d065a497b9, 0x0fef911191df895f}}} \ No newline at end of file
diff --git a/ext/ed25519-amd64-asm/ge25519_base_slide_multiples.data b/ext/ed25519-amd64-asm/ge25519_base_slide_multiples.data
new file mode 100644
index 00000000..32a5d474
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_base_slide_multiples.data
@@ -0,0 +1,96 @@
+{{{0x9d103905d740913e, 0xfd399f05d140beb3, 0xa5c18434688f8a09, 0x44fd2f9298f81267}},
+ {{0x2fbc93c6f58c3b85, 0xcf932dc6fb8c0e19, 0x270b4898643d42c2, 0x07cf9d3a33d4ba65}},
+ {{0xabc91205877aaa68, 0x26d9e823ccaac49e, 0x5a1b7dcbdd43598c, 0x6f117b689f0c65a8}}},
+{{{0x56611fe8a4fcd265, 0x3bd353fde5c1ba7d, 0x8131f31a214bd6bd, 0x2ab91587555bda62}},
+ {{0xaf25b0a84cee9730, 0x025a8430e8864b8a, 0xc11b50029f016732, 0x7a164e1b9a80f8f4}},
+ {{0x14ae933f0dd0d889, 0x589423221c35da62, 0xd170e5458cf2db4c, 0x5a2826af12b9b4c6}}},
+{{{0x7f9182c3a447d6ba, 0xd50014d14b2729b7, 0xe33cf11cb864a087, 0x154a7e73eb1b55f3}},
+ {{0xa212bc4408a5bb33, 0x8d5048c3c75eed02, 0xdd1beb0c5abfec44, 0x2945ccf146e206eb}},
+ {{0xbcbbdbf1812a8285, 0x270e0807d0bdd1fc, 0xb41b670b1bbda72d, 0x43aabe696b3bb69a}}},
+{{{0xba6f2c9aaa3221b1, 0x6ca021533bba23a7, 0x9dea764f92192c3a, 0x1d6edd5d2e5317e0}},
+ {{0x6b1a5cd0944ea3bf, 0x7470353ab39dc0d2, 0x71b2528228542e49, 0x461bea69283c927e}},
+ {{0xf1836dc801b8b3a2, 0xb3035f47053ea49a, 0x529c41ba5877adf3, 0x7a9fbb1c6a0f90a7}}},
+{{{0xf36e217e039d8064, 0x98a081b6f520419b, 0x96cbc608e75eb044, 0x49c05a51fadc9c8f}},
+ {{0x9b2e678aa6a8632f, 0xa6509e6f51bc46c5, 0xceb233c9c686f5b5, 0x34b9ed338add7f59}},
+ {{0x06b4e8bf9045af1b, 0xe2ff83e8a719d22f, 0xaaf6fc2993d4cf16, 0x73c172021b008b06}}},
+{{{0x315f5b0249864348, 0x3ed6b36977088381, 0xa3a075556a8deb95, 0x18ab598029d5c77f}},
+ {{0x2fbf00848a802ade, 0xe5d9fecf02302e27, 0x113e847117703406, 0x4275aae2546d8faf}},
+ {{0xd82b2cc5fd6089e9, 0x031eb4a13282e4a4, 0x44311199b51a8622, 0x3dc65522b53df948}}},
+{{{0x506f013b327fbf93, 0xaefcebc99b776f6b, 0x9d12b232aaad5968, 0x0267882d176024a7}},
+ {{0xbf70c222a2007f6d, 0xbf84b39ab5bcdedb, 0x537a0e12fb07ba07, 0x234fd7eec346f241}},
+ {{0x5360a119732ea378, 0x2437e6b1df8dd471, 0xa2ef37f891a7e533, 0x497ba6fdaa097863}}},
+{{{0x040bcd86468ccf0b, 0xd3829ba42a9910d6, 0x7508300807b25192, 0x43b5cd4218d05ebf}},
+ {{0x24cecc0313cfeaa0, 0x8648c28d189c246d, 0x2dbdbdfac1f2d4d0, 0x61e22917f12de72b}},
+ {{0x5d9a762f9bd0b516, 0xeb38af4e373fdeee, 0x032e5a7d93d64270, 0x511d61210ae4d842}}},
+{{{0x081386484420de87, 0x8a1cf016b592edb4, 0x39fa4e2729942d25, 0x71a7fe6fe2482810}},
+ {{0x92c676ef950e9d81, 0xa54620cdc0d7044f, 0xaa9b36646f8f1248, 0x6d325924ddb855e3}},
+ {{0x6c7182b8a5c8c854, 0x33fd1479fe5f2a03, 0x72cf591883778d0c, 0x4746c4b6559eeaa9}}},
+{{{0x348546c864741147, 0x7d35aedd0efcc849, 0xff939a760672a332, 0x219663497db5e6d6}},
+ {{0xd3777b3c6dc69a2b, 0xdefab2276f89f617, 0x45651cf7b53a16b5, 0x5c9a51de34fe9fb7}},
+ {{0xf510f1cf79f10e67, 0xffdddaa1e658515b, 0x09c3a71710142277, 0x4804503c608223bb}}},
+{{{0x3b6821d23a36d175, 0xbbb40aa7e99b9e32, 0x5d9e5ce420838a47, 0x771e098858de4c5e}},
+ {{0xc4249ed02ca37fc7, 0xa059a0e3a615acab, 0x88a96ed7c96e0e23, 0x553398a51650696d}},
+ {{0x9a12f5d278451edf, 0x3ada5d7985899ccb, 0x477f4a2d9fa59508, 0x5a5ed1d68ff5a611}}},
+{{{0xbae5e0c558527359, 0x392e5c19cadb9d7e, 0x28653c1eda1cabe9, 0x019b60135fefdc44}},
+ {{0x1195122afe150e83, 0xcf209a257e4b35d8, 0x7387f8291e711e20, 0x44acb897d8bf92f0}},
+ {{0x1e6068145e134b83, 0xc4f5e64f24304c16, 0x506e88a8fc1a3ed7, 0x150c49fde6ad2f92}}},
+{{{0xb849863c9cdca868, 0xc83f44dbb8714ad0, 0xfe3ee3560c36168d, 0x78a6d7791e05fbc1}},
+ {{0x8e7bf29509471138, 0x5d6fef394f75a651, 0x10af79c425a708ad, 0x6b2b5a075bb99922}},
+ {{0x58bf704b47a0b976, 0xa601b355741748d5, 0xaa2b1fb1d542f590, 0x725c7ffc4ad55d00}}},
+{{{0x91802bf71cd098c0, 0xfe416ca4ed5e6366, 0xdf585d714902994c, 0x4cd54625f855fae7}},
+ {{0xe4426715d1cf99b2, 0x7352d51102a20d34, 0x23d1157b8b12109f, 0x794cc9277cb1f3a3}},
+ {{0x4af6c426c2ac5053, 0xbc9aedad32f67258, 0x2ad032f10a311021, 0x7008357b6fcc8e85}}},
+{{{0xd01b9fbb82584a34, 0x47ab6463d2b4792b, 0xb631639c48536202, 0x13a92a3669d6d428}},
+ {{0x0b88672738773f01, 0xb8ccc8fa95fbccfb, 0x8d2dd5a3b9ad29b6, 0x06ef7e9851ad0f6a}},
+ {{0xca93771cc0577de5, 0x7540e41e5035dc5c, 0x24680f01d802e071, 0x3c296ddf8a2af86a}}},
+{{{0xfceb4d2ebb1f2541, 0xb89510c740adb91f, 0xfc71a37dd0a1ad05, 0x0a892c700747717b}},
+ {{0xaead15f9d914a713, 0xa92f7bf98c8ff912, 0xaff823179f53d730, 0x7a99d393490c77ba}},
+ {{0x8f52ed2436bda3e8, 0x77a8c84157e80794, 0xa5a96563262f9ce0, 0x286762d28302f7d2}}},
+{{{0x7c558e2bce2ef5bd, 0xe4986cb46747bc63, 0x154a179f3bbb89b8, 0x7686f2a3d6f1767a}},
+ {{0x4e7836093ce35b25, 0x82e1181db26baa97, 0x0cc192d3cbc7b83f, 0x32f1da046a9d9d3a}},
+ {{0xaa8d12a66d597c6a, 0x8f11930304d3852b, 0x3f91dc73c209b022, 0x561305f8a9ad28a6}}},
+{{{0x6722cc28e7b0c0d5, 0x709de9bbdb075c53, 0xcaf68da7d7010a61, 0x030a1aef2c57cc6c}},
+ {{0x100c978dec92aed1, 0xca43d5434d6d73e5, 0x83131b22d847ba48, 0x00aaec53e35d4d2c}},
+ {{0x7bb1f773003ad2aa, 0x0b3f29802b216608, 0x7821dc86520ed23e, 0x20be9c1c24065480}}},
+{{{0x20e0e44ae2025e60, 0xb03b3b2fcbdcb938, 0x105d639cf95a0d1c, 0x69764c545067e311}},
+ {{0xe15387d8249673a6, 0x5943bc2df546e493, 0x1c7f9a81c36f63b5, 0x750ab3361f0ac1de}},
+ {{0x1e8a3283a2f81037, 0x6f2eda23bd7fcbf1, 0xb72fd15bac2e2563, 0x54f96b3fb7075040}}},
+{{{0x177dafc616b11ecd, 0x89764b9cfa576479, 0xb7a8a110e6ece785, 0x78e6839fbe85dbf0}},
+ {{0x0fadf20429669279, 0x3adda2047d7d724a, 0x6f3d94828c5760f1, 0x3d7fe9c52bb7539e}},
+ {{0x70332df737b8856b, 0x75d05d43041a178a, 0x320ff74aa0e59e22, 0x70f268f350088242}}},
+{{{0x2324112070dcf355, 0x380cc97ee7fce117, 0xb31ddeed3552b698, 0x404e56c039b8c4b9}},
+ {{0x66864583b1805f47, 0xf535c5d160dd7c19, 0xe9874eb71e4cb006, 0x7c0d345cfad889d9}},
+ {{0x591f1f4b8c78338a, 0xa0366ab167e0b5e1, 0x5cbc4152b45f3d44, 0x20d754762aaec777}}},
+{{{0x9d74feb135b9f543, 0x84b37df1de8c956c, 0xe9322b0757138ba9, 0x38b8ada8790b4ce1}},
+ {{0x5e8fc36fc73bb758, 0xace543a5363cbb9a, 0xa9934a7d903bc922, 0x2b8f1e46f3ceec62}},
+ {{0xb5c04a9cdf51f95d, 0x2b3952aecb1fdeac, 0x1d106d8b328b66da, 0x049aeb32ceba1953}}},
+{{{0xd7767d3c63dcfe7e, 0x209c594897856e40, 0xb6676861e14f7c13, 0x51c665e0c8d625fc}},
+ {{0xaa507d0b75fc7931, 0x0fef924b7a6725d3, 0x1d82542b396b3930, 0x795ee17530f674fc}},
+ {{0x254a5b0a52ecbd81, 0x5d411f6ee034afe7, 0xe6a24d0dcaee4a31, 0x6cd19bf49dc54477}}},
+{{{0x7e87619052179ca3, 0x571d0a060b2c9f85, 0x80a2baa88499711e, 0x7520f3db40b2e638}},
+ {{0x1ffe612165afc386, 0x082a2a88b8d51b10, 0x76f6627e20990baa, 0x5e01b3a7429e43e7}},
+ {{0x3db50be3d39357a1, 0x967b6cdd599e94a5, 0x1a309a64df311e6e, 0x71092c9ccef3c986}}},
+{{{0x53d8523f0364918c, 0xa2b404f43fab6b1c, 0x080b4a9e6681e5a4, 0x0ea15b03d0257ba7}},
+ {{0x856bd8ac74051dcf, 0x03f6a40855b7aa1e, 0x3a4ae7cbc9743ceb, 0x4173a5bb7137abde}},
+ {{0x17c56e31f0f9218a, 0x5a696e2b1afc4708, 0xf7931668f4b2f176, 0x5fc565614a4e3a67}}},
+{{{0x136e570dc46d7ae5, 0x0fd0aacc54f8dc8f, 0x59549f03310dad86, 0x62711c414c454aa1}},
+ {{0x4892e1e67790988e, 0x01d5950f1c5cd722, 0xe3b0819ae5923eed, 0x3214c7409d46651b}},
+ {{0x1329827406651770, 0x3ba4a0668a279436, 0xd9b6b8ec185d223c, 0x5bea94073ecb833c}}},
+{{{0x641dbf0912c89be4, 0xacf38b317d6e579c, 0xabfe9e02f697b065, 0x3aacd5c148f61eec}},
+ {{0xb470ce63f343d2f8, 0x0067ba8f0543e8f1, 0x35da51a1a2117b6f, 0x4ad0785944f1bd2f}},
+ {{0x858e3b34c3318301, 0xdc99c04707316826, 0x34085b2ed39da88c, 0x3aff0cb1d902853d}}},
+{{{0x87c5c7eb3a20405e, 0x8ee311efedad56c9, 0x29252e48ad29d5f9, 0x110e7e86f4cd251d}},
+ {{0x9226430bf4c53505, 0x68e49c13261f2283, 0x09ef33788fd327c6, 0x2ccf9f732bd99e7f}},
+ {{0x57c0d89ed603f5e4, 0x12888628f0b0200c, 0x53172709a02e3bb7, 0x05c557e0b9693a37}}},
+{{{0xd8f9ce311fc97e6f, 0x7a3f263011f9fdae, 0xe15b7ea08bed25dd, 0x6e154c178fe9875a}},
+ {{0xf776bbb089c20eb0, 0x61f85bf6fa0fd85c, 0xb6b93f4e634421fb, 0x289fef0841861205}},
+ {{0xcf616336fed69abf, 0x9b16e4e78335c94f, 0x13789765753a7fe7, 0x6afbf642a95ca319}}},
+{{{0x7da8de0c62f5d2c1, 0x98fc3da4b00e7b9a, 0x7deb6ada0dad70e0, 0x0db4b851b95038c4}},
+ {{0x5de55070f913a8cc, 0x7d1d167b2b0cf561, 0xda2956b690ead489, 0x12c093cedb801ed9}},
+ {{0xfc147f9308b8190f, 0x06969da0a11ae310, 0xcee75572dac7d7fd, 0x33aa8799c6635ce6}}},
+{{{0xaf0ff51ebd085cf2, 0x78f51a8967d33f1f, 0x6ec2bfe15060033c, 0x233c6f29e8e21a86}},
+ {{0x8348f588fc156cb1, 0x6da2ba9b1a0a6d27, 0xe2262d5c87ca5ab6, 0x212cd0c1c8d589a6}},
+ {{0xd2f4d5107f18c781, 0x122ecdf2527e9d28, 0xa70a862a3d3d3341, 0x1db7778911914ce3}}},
+{{{0xddf352397c6bc26f, 0x7a97e2cc53d50113, 0x7c74f43abf79a330, 0x31ad97ad26e2adfc}},
+ {{0xb3394769dd701ab6, 0xe2b8ded419cf8da5, 0x15df4161fd2ac852, 0x7ae2ca8a017d24be}},
+ {{0xb7e817ed0920b962, 0x1e8518cc3f19da9d, 0xe491c14f25560a64, 0x1ed1fc53a6622c83}}} \ No newline at end of file
diff --git a/ext/ed25519-amd64-asm/ge25519_dbl_p1p1.s b/ext/ed25519-amd64-asm/ge25519_dbl_p1p1.s
new file mode 100644
index 00000000..7909a988
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_dbl_p1p1.s
@@ -0,0 +1,2975 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 pp
+
+# qhasm: input rp
+
+# qhasm: input pp
+
+# qhasm: int64 a0
+
+# qhasm: int64 a1
+
+# qhasm: int64 a2
+
+# qhasm: int64 a3
+
+# qhasm: stack64 a0_stack
+
+# qhasm: stack64 a1_stack
+
+# qhasm: stack64 a2_stack
+
+# qhasm: stack64 a3_stack
+
+# qhasm: int64 b0
+
+# qhasm: int64 b1
+
+# qhasm: int64 b2
+
+# qhasm: int64 b3
+
+# qhasm: stack64 b0_stack
+
+# qhasm: stack64 b1_stack
+
+# qhasm: stack64 b2_stack
+
+# qhasm: stack64 b3_stack
+
+# qhasm: int64 c0
+
+# qhasm: int64 c1
+
+# qhasm: int64 c2
+
+# qhasm: int64 c3
+
+# qhasm: stack64 c0_stack
+
+# qhasm: stack64 c1_stack
+
+# qhasm: stack64 c2_stack
+
+# qhasm: stack64 c3_stack
+
+# qhasm: int64 d0
+
+# qhasm: int64 d1
+
+# qhasm: int64 d2
+
+# qhasm: int64 d3
+
+# qhasm: stack64 d0_stack
+
+# qhasm: stack64 d1_stack
+
+# qhasm: stack64 d2_stack
+
+# qhasm: stack64 d3_stack
+
+# qhasm: int64 e0
+
+# qhasm: int64 e1
+
+# qhasm: int64 e2
+
+# qhasm: int64 e3
+
+# qhasm: stack64 e0_stack
+
+# qhasm: stack64 e1_stack
+
+# qhasm: stack64 e2_stack
+
+# qhasm: stack64 e3_stack
+
+# qhasm: int64 rx0
+
+# qhasm: int64 rx1
+
+# qhasm: int64 rx2
+
+# qhasm: int64 rx3
+
+# qhasm: stack64 rx0_stack
+
+# qhasm: stack64 rx1_stack
+
+# qhasm: stack64 rx2_stack
+
+# qhasm: stack64 rx3_stack
+
+# qhasm: int64 ry0
+
+# qhasm: int64 ry1
+
+# qhasm: int64 ry2
+
+# qhasm: int64 ry3
+
+# qhasm: int64 ry4
+
+# qhasm: int64 rz0
+
+# qhasm: int64 rz1
+
+# qhasm: int64 rz2
+
+# qhasm: int64 rz3
+
+# qhasm: int64 rt0
+
+# qhasm: int64 rt1
+
+# qhasm: int64 rt2
+
+# qhasm: int64 rt3
+
+# qhasm: int64 mulr4
+
+# qhasm: int64 mulr5
+
+# qhasm: int64 mulr6
+
+# qhasm: int64 mulr7
+
+# qhasm: int64 mulr8
+
+# qhasm: int64 mulrax
+
+# qhasm: int64 mulrdx
+
+# qhasm: int64 mulx0
+
+# qhasm: int64 mulx1
+
+# qhasm: int64 mulx2
+
+# qhasm: int64 mulx3
+
+# qhasm: int64 mulc
+
+# qhasm: int64 mulzero
+
+# qhasm: int64 muli38
+
+# qhasm: int64 squarer4
+
+# qhasm: int64 squarer5
+
+# qhasm: int64 squarer6
+
+# qhasm: int64 squarer7
+
+# qhasm: int64 squarer8
+
+# qhasm: int64 squarerax
+
+# qhasm: int64 squarerdx
+
+# qhasm: int64 squaret1
+
+# qhasm: int64 squaret2
+
+# qhasm: int64 squaret3
+
+# qhasm: int64 squarec
+
+# qhasm: int64 squarezero
+
+# qhasm: int64 squarei38
+
+# qhasm: int64 addt0
+
+# qhasm: int64 addt1
+
+# qhasm: int64 subt0
+
+# qhasm: int64 subt1
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_ge25519_dbl_p1p1
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_ge25519_dbl_p1p1
+.globl crypto_sign_ed25519_amd64_64_ge25519_dbl_p1p1
+_crypto_sign_ed25519_amd64_64_ge25519_dbl_p1p1:
+crypto_sign_ed25519_amd64_64_ge25519_dbl_p1p1:
+mov %rsp,%r11
+and $31,%r11
+add $192,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: squarer7 = 0
+# asm 1: mov $0,>squarer7=int64#4
+# asm 2: mov $0,>squarer7=%rcx
+mov $0,%rcx
+
+# qhasm: squarerax = *(uint64 *)(pp + 8)
+# asm 1: movq 8(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 8(<pp=%rsi),>squarerax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 0)
+# asm 1: mulq 0(<pp=int64#2)
+# asm 2: mulq 0(<pp=%rsi)
+mulq 0(%rsi)
+
+# qhasm: a1 = squarerax
+# asm 1: mov <squarerax=int64#7,>a1=int64#5
+# asm 2: mov <squarerax=%rax,>a1=%r8
+mov %rax,%r8
+
+# qhasm: a2 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>a2=int64#6
+# asm 2: mov <squarerdx=%rdx,>a2=%r9
+mov %rdx,%r9
+
+# qhasm: squarerax = *(uint64 *)(pp + 16)
+# asm 1: movq 16(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 16(<pp=%rsi),>squarerax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 8)
+# asm 1: mulq 8(<pp=int64#2)
+# asm 2: mulq 8(<pp=%rsi)
+mulq 8(%rsi)
+
+# qhasm: a3 = squarerax
+# asm 1: mov <squarerax=int64#7,>a3=int64#8
+# asm 2: mov <squarerax=%rax,>a3=%r10
+mov %rax,%r10
+
+# qhasm: squarer4 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer4=int64#9
+# asm 2: mov <squarerdx=%rdx,>squarer4=%r11
+mov %rdx,%r11
+
+# qhasm: squarerax = *(uint64 *)(pp + 24)
+# asm 1: movq 24(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 24(<pp=%rsi),>squarerax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 16)
+# asm 1: mulq 16(<pp=int64#2)
+# asm 2: mulq 16(<pp=%rsi)
+mulq 16(%rsi)
+
+# qhasm: squarer5 = squarerax
+# asm 1: mov <squarerax=int64#7,>squarer5=int64#10
+# asm 2: mov <squarerax=%rax,>squarer5=%r12
+mov %rax,%r12
+
+# qhasm: squarer6 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer6=int64#11
+# asm 2: mov <squarerdx=%rdx,>squarer6=%r13
+mov %rdx,%r13
+
+# qhasm: squarerax = *(uint64 *)(pp + 16)
+# asm 1: movq 16(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 16(<pp=%rsi),>squarerax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 0)
+# asm 1: mulq 0(<pp=int64#2)
+# asm 2: mulq 0(<pp=%rsi)
+mulq 0(%rsi)
+
+# qhasm: carry? a2 += squarerax
+# asm 1: add <squarerax=int64#7,<a2=int64#6
+# asm 2: add <squarerax=%rax,<a2=%r9
+add %rax,%r9
+
+# qhasm: carry? a3 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<a3=int64#8
+# asm 2: adc <squarerdx=%rdx,<a3=%r10
+adc %rdx,%r10
+
+# qhasm: squarer4 += 0 + carry
+# asm 1: adc $0,<squarer4=int64#9
+# asm 2: adc $0,<squarer4=%r11
+adc $0,%r11
+
+# qhasm: squarerax = *(uint64 *)(pp + 24)
+# asm 1: movq 24(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 24(<pp=%rsi),>squarerax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 8)
+# asm 1: mulq 8(<pp=int64#2)
+# asm 2: mulq 8(<pp=%rsi)
+mulq 8(%rsi)
+
+# qhasm: carry? squarer4 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer4=int64#9
+# asm 2: add <squarerax=%rax,<squarer4=%r11
+add %rax,%r11
+
+# qhasm: carry? squarer5 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer5=int64#10
+# asm 2: adc <squarerdx=%rdx,<squarer5=%r12
+adc %rdx,%r12
+
+# qhasm: squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarerax = *(uint64 *)(pp + 24)
+# asm 1: movq 24(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 24(<pp=%rsi),>squarerax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 0)
+# asm 1: mulq 0(<pp=int64#2)
+# asm 2: mulq 0(<pp=%rsi)
+mulq 0(%rsi)
+
+# qhasm: carry? a3 += squarerax
+# asm 1: add <squarerax=int64#7,<a3=int64#8
+# asm 2: add <squarerax=%rax,<a3=%r10
+add %rax,%r10
+
+# qhasm: carry? squarer4 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer4=int64#9
+# asm 2: adc <squarerdx=%rdx,<squarer4=%r11
+adc %rdx,%r11
+
+# qhasm: carry? squarer5 += 0 + carry
+# asm 1: adc $0,<squarer5=int64#10
+# asm 2: adc $0,<squarer5=%r12
+adc $0,%r12
+
+# qhasm: carry? squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarer7 += 0 + carry
+# asm 1: adc $0,<squarer7=int64#4
+# asm 2: adc $0,<squarer7=%rcx
+adc $0,%rcx
+
+# qhasm: carry? a1 += a1
+# asm 1: add <a1=int64#5,<a1=int64#5
+# asm 2: add <a1=%r8,<a1=%r8
+add %r8,%r8
+
+# qhasm: carry? a2 += a2 + carry
+# asm 1: adc <a2=int64#6,<a2=int64#6
+# asm 2: adc <a2=%r9,<a2=%r9
+adc %r9,%r9
+
+# qhasm: carry? a3 += a3 + carry
+# asm 1: adc <a3=int64#8,<a3=int64#8
+# asm 2: adc <a3=%r10,<a3=%r10
+adc %r10,%r10
+
+# qhasm: carry? squarer4 += squarer4 + carry
+# asm 1: adc <squarer4=int64#9,<squarer4=int64#9
+# asm 2: adc <squarer4=%r11,<squarer4=%r11
+adc %r11,%r11
+
+# qhasm: carry? squarer5 += squarer5 + carry
+# asm 1: adc <squarer5=int64#10,<squarer5=int64#10
+# asm 2: adc <squarer5=%r12,<squarer5=%r12
+adc %r12,%r12
+
+# qhasm: carry? squarer6 += squarer6 + carry
+# asm 1: adc <squarer6=int64#11,<squarer6=int64#11
+# asm 2: adc <squarer6=%r13,<squarer6=%r13
+adc %r13,%r13
+
+# qhasm: squarer7 += squarer7 + carry
+# asm 1: adc <squarer7=int64#4,<squarer7=int64#4
+# asm 2: adc <squarer7=%rcx,<squarer7=%rcx
+adc %rcx,%rcx
+
+# qhasm: squarerax = *(uint64 *)(pp + 0)
+# asm 1: movq 0(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 0(<pp=%rsi),>squarerax=%rax
+movq 0(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 0)
+# asm 1: mulq 0(<pp=int64#2)
+# asm 2: mulq 0(<pp=%rsi)
+mulq 0(%rsi)
+
+# qhasm: a0 = squarerax
+# asm 1: mov <squarerax=int64#7,>a0=int64#12
+# asm 2: mov <squarerax=%rax,>a0=%r14
+mov %rax,%r14
+
+# qhasm: squaret1 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squaret1=int64#13
+# asm 2: mov <squarerdx=%rdx,>squaret1=%r15
+mov %rdx,%r15
+
+# qhasm: squarerax = *(uint64 *)(pp + 8)
+# asm 1: movq 8(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 8(<pp=%rsi),>squarerax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 8)
+# asm 1: mulq 8(<pp=int64#2)
+# asm 2: mulq 8(<pp=%rsi)
+mulq 8(%rsi)
+
+# qhasm: squaret2 = squarerax
+# asm 1: mov <squarerax=int64#7,>squaret2=int64#14
+# asm 2: mov <squarerax=%rax,>squaret2=%rbx
+mov %rax,%rbx
+
+# qhasm: squaret3 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squaret3=int64#15
+# asm 2: mov <squarerdx=%rdx,>squaret3=%rbp
+mov %rdx,%rbp
+
+# qhasm: squarerax = *(uint64 *)(pp + 16)
+# asm 1: movq 16(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 16(<pp=%rsi),>squarerax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 16)
+# asm 1: mulq 16(<pp=int64#2)
+# asm 2: mulq 16(<pp=%rsi)
+mulq 16(%rsi)
+
+# qhasm: carry? a1 += squaret1
+# asm 1: add <squaret1=int64#13,<a1=int64#5
+# asm 2: add <squaret1=%r15,<a1=%r8
+add %r15,%r8
+
+# qhasm: carry? a2 += squaret2 + carry
+# asm 1: adc <squaret2=int64#14,<a2=int64#6
+# asm 2: adc <squaret2=%rbx,<a2=%r9
+adc %rbx,%r9
+
+# qhasm: carry? a3 += squaret3 + carry
+# asm 1: adc <squaret3=int64#15,<a3=int64#8
+# asm 2: adc <squaret3=%rbp,<a3=%r10
+adc %rbp,%r10
+
+# qhasm: carry? squarer4 += squarerax + carry
+# asm 1: adc <squarerax=int64#7,<squarer4=int64#9
+# asm 2: adc <squarerax=%rax,<squarer4=%r11
+adc %rax,%r11
+
+# qhasm: carry? squarer5 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer5=int64#10
+# asm 2: adc <squarerdx=%rdx,<squarer5=%r12
+adc %rdx,%r12
+
+# qhasm: carry? squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarer7 += 0 + carry
+# asm 1: adc $0,<squarer7=int64#4
+# asm 2: adc $0,<squarer7=%rcx
+adc $0,%rcx
+
+# qhasm: squarerax = *(uint64 *)(pp + 24)
+# asm 1: movq 24(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 24(<pp=%rsi),>squarerax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 24)
+# asm 1: mulq 24(<pp=int64#2)
+# asm 2: mulq 24(<pp=%rsi)
+mulq 24(%rsi)
+
+# qhasm: carry? squarer6 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer6=int64#11
+# asm 2: add <squarerax=%rax,<squarer6=%r13
+add %rax,%r13
+
+# qhasm: squarer7 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer7=int64#4
+# asm 2: adc <squarerdx=%rdx,<squarer7=%rcx
+adc %rdx,%rcx
+
+# qhasm: squarerax = squarer4
+# asm 1: mov <squarer4=int64#9,>squarerax=int64#7
+# asm 2: mov <squarer4=%r11,>squarerax=%rax
+mov %r11,%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: squarer4 = squarerax
+# asm 1: mov <squarerax=int64#7,>squarer4=int64#9
+# asm 2: mov <squarerax=%rax,>squarer4=%r11
+mov %rax,%r11
+
+# qhasm: squarerax = squarer5
+# asm 1: mov <squarer5=int64#10,>squarerax=int64#7
+# asm 2: mov <squarer5=%r12,>squarerax=%rax
+mov %r12,%rax
+
+# qhasm: squarer5 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer5=int64#10
+# asm 2: mov <squarerdx=%rdx,>squarer5=%r12
+mov %rdx,%r12
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? squarer5 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer5=int64#10
+# asm 2: add <squarerax=%rax,<squarer5=%r12
+add %rax,%r12
+
+# qhasm: squarerax = squarer6
+# asm 1: mov <squarer6=int64#11,>squarerax=int64#7
+# asm 2: mov <squarer6=%r13,>squarerax=%rax
+mov %r13,%rax
+
+# qhasm: squarer6 = 0
+# asm 1: mov $0,>squarer6=int64#11
+# asm 2: mov $0,>squarer6=%r13
+mov $0,%r13
+
+# qhasm: squarer6 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer6=int64#11
+# asm 2: adc <squarerdx=%rdx,<squarer6=%r13
+adc %rdx,%r13
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? squarer6 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer6=int64#11
+# asm 2: add <squarerax=%rax,<squarer6=%r13
+add %rax,%r13
+
+# qhasm: squarerax = squarer7
+# asm 1: mov <squarer7=int64#4,>squarerax=int64#7
+# asm 2: mov <squarer7=%rcx,>squarerax=%rax
+mov %rcx,%rax
+
+# qhasm: squarer7 = 0
+# asm 1: mov $0,>squarer7=int64#4
+# asm 2: mov $0,>squarer7=%rcx
+mov $0,%rcx
+
+# qhasm: squarer7 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer7=int64#4
+# asm 2: adc <squarerdx=%rdx,<squarer7=%rcx
+adc %rdx,%rcx
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? squarer7 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer7=int64#4
+# asm 2: add <squarerax=%rax,<squarer7=%rcx
+add %rax,%rcx
+
+# qhasm: squarer8 = 0
+# asm 1: mov $0,>squarer8=int64#7
+# asm 2: mov $0,>squarer8=%rax
+mov $0,%rax
+
+# qhasm: squarer8 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer8=int64#7
+# asm 2: adc <squarerdx=%rdx,<squarer8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? a0 += squarer4
+# asm 1: add <squarer4=int64#9,<a0=int64#12
+# asm 2: add <squarer4=%r11,<a0=%r14
+add %r11,%r14
+
+# qhasm: carry? a1 += squarer5 + carry
+# asm 1: adc <squarer5=int64#10,<a1=int64#5
+# asm 2: adc <squarer5=%r12,<a1=%r8
+adc %r12,%r8
+
+# qhasm: carry? a2 += squarer6 + carry
+# asm 1: adc <squarer6=int64#11,<a2=int64#6
+# asm 2: adc <squarer6=%r13,<a2=%r9
+adc %r13,%r9
+
+# qhasm: carry? a3 += squarer7 + carry
+# asm 1: adc <squarer7=int64#4,<a3=int64#8
+# asm 2: adc <squarer7=%rcx,<a3=%r10
+adc %rcx,%r10
+
+# qhasm: squarezero = 0
+# asm 1: mov $0,>squarezero=int64#3
+# asm 2: mov $0,>squarezero=%rdx
+mov $0,%rdx
+
+# qhasm: squarer8 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<squarer8=int64#7
+# asm 2: adc <squarezero=%rdx,<squarer8=%rax
+adc %rdx,%rax
+
+# qhasm: squarer8 *= 38
+# asm 1: imulq $38,<squarer8=int64#7,>squarer8=int64#4
+# asm 2: imulq $38,<squarer8=%rax,>squarer8=%rcx
+imulq $38,%rax,%rcx
+
+# qhasm: carry? a0 += squarer8
+# asm 1: add <squarer8=int64#4,<a0=int64#12
+# asm 2: add <squarer8=%rcx,<a0=%r14
+add %rcx,%r14
+
+# qhasm: carry? a1 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<a1=int64#5
+# asm 2: adc <squarezero=%rdx,<a1=%r8
+adc %rdx,%r8
+
+# qhasm: carry? a2 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<a2=int64#6
+# asm 2: adc <squarezero=%rdx,<a2=%r9
+adc %rdx,%r9
+
+# qhasm: carry? a3 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<a3=int64#8
+# asm 2: adc <squarezero=%rdx,<a3=%r10
+adc %rdx,%r10
+
+# qhasm: squarezero += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<squarezero=int64#3
+# asm 2: adc <squarezero=%rdx,<squarezero=%rdx
+adc %rdx,%rdx
+
+# qhasm: squarezero *= 38
+# asm 1: imulq $38,<squarezero=int64#3,>squarezero=int64#3
+# asm 2: imulq $38,<squarezero=%rdx,>squarezero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: a0 += squarezero
+# asm 1: add <squarezero=int64#3,<a0=int64#12
+# asm 2: add <squarezero=%rdx,<a0=%r14
+add %rdx,%r14
+
+# qhasm: a0_stack = a0
+# asm 1: movq <a0=int64#12,>a0_stack=stack64#8
+# asm 2: movq <a0=%r14,>a0_stack=56(%rsp)
+movq %r14,56(%rsp)
+
+# qhasm: a1_stack = a1
+# asm 1: movq <a1=int64#5,>a1_stack=stack64#9
+# asm 2: movq <a1=%r8,>a1_stack=64(%rsp)
+movq %r8,64(%rsp)
+
+# qhasm: a2_stack = a2
+# asm 1: movq <a2=int64#6,>a2_stack=stack64#10
+# asm 2: movq <a2=%r9,>a2_stack=72(%rsp)
+movq %r9,72(%rsp)
+
+# qhasm: a3_stack = a3
+# asm 1: movq <a3=int64#8,>a3_stack=stack64#11
+# asm 2: movq <a3=%r10,>a3_stack=80(%rsp)
+movq %r10,80(%rsp)
+
+# qhasm: squarer7 = 0
+# asm 1: mov $0,>squarer7=int64#4
+# asm 2: mov $0,>squarer7=%rcx
+mov $0,%rcx
+
+# qhasm: squarerax = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 40(<pp=%rsi),>squarerax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 32)
+# asm 1: mulq 32(<pp=int64#2)
+# asm 2: mulq 32(<pp=%rsi)
+mulq 32(%rsi)
+
+# qhasm: b1 = squarerax
+# asm 1: mov <squarerax=int64#7,>b1=int64#5
+# asm 2: mov <squarerax=%rax,>b1=%r8
+mov %rax,%r8
+
+# qhasm: b2 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>b2=int64#6
+# asm 2: mov <squarerdx=%rdx,>b2=%r9
+mov %rdx,%r9
+
+# qhasm: squarerax = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 48(<pp=%rsi),>squarerax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 40)
+# asm 1: mulq 40(<pp=int64#2)
+# asm 2: mulq 40(<pp=%rsi)
+mulq 40(%rsi)
+
+# qhasm: b3 = squarerax
+# asm 1: mov <squarerax=int64#7,>b3=int64#8
+# asm 2: mov <squarerax=%rax,>b3=%r10
+mov %rax,%r10
+
+# qhasm: squarer4 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer4=int64#9
+# asm 2: mov <squarerdx=%rdx,>squarer4=%r11
+mov %rdx,%r11
+
+# qhasm: squarerax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 56(<pp=%rsi),>squarerax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 48)
+# asm 1: mulq 48(<pp=int64#2)
+# asm 2: mulq 48(<pp=%rsi)
+mulq 48(%rsi)
+
+# qhasm: squarer5 = squarerax
+# asm 1: mov <squarerax=int64#7,>squarer5=int64#10
+# asm 2: mov <squarerax=%rax,>squarer5=%r12
+mov %rax,%r12
+
+# qhasm: squarer6 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer6=int64#11
+# asm 2: mov <squarerdx=%rdx,>squarer6=%r13
+mov %rdx,%r13
+
+# qhasm: squarerax = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 48(<pp=%rsi),>squarerax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 32)
+# asm 1: mulq 32(<pp=int64#2)
+# asm 2: mulq 32(<pp=%rsi)
+mulq 32(%rsi)
+
+# qhasm: carry? b2 += squarerax
+# asm 1: add <squarerax=int64#7,<b2=int64#6
+# asm 2: add <squarerax=%rax,<b2=%r9
+add %rax,%r9
+
+# qhasm: carry? b3 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<b3=int64#8
+# asm 2: adc <squarerdx=%rdx,<b3=%r10
+adc %rdx,%r10
+
+# qhasm: squarer4 += 0 + carry
+# asm 1: adc $0,<squarer4=int64#9
+# asm 2: adc $0,<squarer4=%r11
+adc $0,%r11
+
+# qhasm: squarerax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 56(<pp=%rsi),>squarerax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 40)
+# asm 1: mulq 40(<pp=int64#2)
+# asm 2: mulq 40(<pp=%rsi)
+mulq 40(%rsi)
+
+# qhasm: carry? squarer4 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer4=int64#9
+# asm 2: add <squarerax=%rax,<squarer4=%r11
+add %rax,%r11
+
+# qhasm: carry? squarer5 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer5=int64#10
+# asm 2: adc <squarerdx=%rdx,<squarer5=%r12
+adc %rdx,%r12
+
+# qhasm: squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarerax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 56(<pp=%rsi),>squarerax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 32)
+# asm 1: mulq 32(<pp=int64#2)
+# asm 2: mulq 32(<pp=%rsi)
+mulq 32(%rsi)
+
+# qhasm: carry? b3 += squarerax
+# asm 1: add <squarerax=int64#7,<b3=int64#8
+# asm 2: add <squarerax=%rax,<b3=%r10
+add %rax,%r10
+
+# qhasm: carry? squarer4 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer4=int64#9
+# asm 2: adc <squarerdx=%rdx,<squarer4=%r11
+adc %rdx,%r11
+
+# qhasm: carry? squarer5 += 0 + carry
+# asm 1: adc $0,<squarer5=int64#10
+# asm 2: adc $0,<squarer5=%r12
+adc $0,%r12
+
+# qhasm: carry? squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarer7 += 0 + carry
+# asm 1: adc $0,<squarer7=int64#4
+# asm 2: adc $0,<squarer7=%rcx
+adc $0,%rcx
+
+# qhasm: carry? b1 += b1
+# asm 1: add <b1=int64#5,<b1=int64#5
+# asm 2: add <b1=%r8,<b1=%r8
+add %r8,%r8
+
+# qhasm: carry? b2 += b2 + carry
+# asm 1: adc <b2=int64#6,<b2=int64#6
+# asm 2: adc <b2=%r9,<b2=%r9
+adc %r9,%r9
+
+# qhasm: carry? b3 += b3 + carry
+# asm 1: adc <b3=int64#8,<b3=int64#8
+# asm 2: adc <b3=%r10,<b3=%r10
+adc %r10,%r10
+
+# qhasm: carry? squarer4 += squarer4 + carry
+# asm 1: adc <squarer4=int64#9,<squarer4=int64#9
+# asm 2: adc <squarer4=%r11,<squarer4=%r11
+adc %r11,%r11
+
+# qhasm: carry? squarer5 += squarer5 + carry
+# asm 1: adc <squarer5=int64#10,<squarer5=int64#10
+# asm 2: adc <squarer5=%r12,<squarer5=%r12
+adc %r12,%r12
+
+# qhasm: carry? squarer6 += squarer6 + carry
+# asm 1: adc <squarer6=int64#11,<squarer6=int64#11
+# asm 2: adc <squarer6=%r13,<squarer6=%r13
+adc %r13,%r13
+
+# qhasm: squarer7 += squarer7 + carry
+# asm 1: adc <squarer7=int64#4,<squarer7=int64#4
+# asm 2: adc <squarer7=%rcx,<squarer7=%rcx
+adc %rcx,%rcx
+
+# qhasm: squarerax = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 32(<pp=%rsi),>squarerax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 32)
+# asm 1: mulq 32(<pp=int64#2)
+# asm 2: mulq 32(<pp=%rsi)
+mulq 32(%rsi)
+
+# qhasm: b0 = squarerax
+# asm 1: mov <squarerax=int64#7,>b0=int64#12
+# asm 2: mov <squarerax=%rax,>b0=%r14
+mov %rax,%r14
+
+# qhasm: squaret1 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squaret1=int64#13
+# asm 2: mov <squarerdx=%rdx,>squaret1=%r15
+mov %rdx,%r15
+
+# qhasm: squarerax = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 40(<pp=%rsi),>squarerax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 40)
+# asm 1: mulq 40(<pp=int64#2)
+# asm 2: mulq 40(<pp=%rsi)
+mulq 40(%rsi)
+
+# qhasm: squaret2 = squarerax
+# asm 1: mov <squarerax=int64#7,>squaret2=int64#14
+# asm 2: mov <squarerax=%rax,>squaret2=%rbx
+mov %rax,%rbx
+
+# qhasm: squaret3 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squaret3=int64#15
+# asm 2: mov <squarerdx=%rdx,>squaret3=%rbp
+mov %rdx,%rbp
+
+# qhasm: squarerax = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 48(<pp=%rsi),>squarerax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 48)
+# asm 1: mulq 48(<pp=int64#2)
+# asm 2: mulq 48(<pp=%rsi)
+mulq 48(%rsi)
+
+# qhasm: carry? b1 += squaret1
+# asm 1: add <squaret1=int64#13,<b1=int64#5
+# asm 2: add <squaret1=%r15,<b1=%r8
+add %r15,%r8
+
+# qhasm: carry? b2 += squaret2 + carry
+# asm 1: adc <squaret2=int64#14,<b2=int64#6
+# asm 2: adc <squaret2=%rbx,<b2=%r9
+adc %rbx,%r9
+
+# qhasm: carry? b3 += squaret3 + carry
+# asm 1: adc <squaret3=int64#15,<b3=int64#8
+# asm 2: adc <squaret3=%rbp,<b3=%r10
+adc %rbp,%r10
+
+# qhasm: carry? squarer4 += squarerax + carry
+# asm 1: adc <squarerax=int64#7,<squarer4=int64#9
+# asm 2: adc <squarerax=%rax,<squarer4=%r11
+adc %rax,%r11
+
+# qhasm: carry? squarer5 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer5=int64#10
+# asm 2: adc <squarerdx=%rdx,<squarer5=%r12
+adc %rdx,%r12
+
+# qhasm: carry? squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarer7 += 0 + carry
+# asm 1: adc $0,<squarer7=int64#4
+# asm 2: adc $0,<squarer7=%rcx
+adc $0,%rcx
+
+# qhasm: squarerax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 56(<pp=%rsi),>squarerax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 56)
+# asm 1: mulq 56(<pp=int64#2)
+# asm 2: mulq 56(<pp=%rsi)
+mulq 56(%rsi)
+
+# qhasm: carry? squarer6 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer6=int64#11
+# asm 2: add <squarerax=%rax,<squarer6=%r13
+add %rax,%r13
+
+# qhasm: squarer7 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer7=int64#4
+# asm 2: adc <squarerdx=%rdx,<squarer7=%rcx
+adc %rdx,%rcx
+
+# qhasm: squarerax = squarer4
+# asm 1: mov <squarer4=int64#9,>squarerax=int64#7
+# asm 2: mov <squarer4=%r11,>squarerax=%rax
+mov %r11,%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: squarer4 = squarerax
+# asm 1: mov <squarerax=int64#7,>squarer4=int64#9
+# asm 2: mov <squarerax=%rax,>squarer4=%r11
+mov %rax,%r11
+
+# qhasm: squarerax = squarer5
+# asm 1: mov <squarer5=int64#10,>squarerax=int64#7
+# asm 2: mov <squarer5=%r12,>squarerax=%rax
+mov %r12,%rax
+
+# qhasm: squarer5 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer5=int64#10
+# asm 2: mov <squarerdx=%rdx,>squarer5=%r12
+mov %rdx,%r12
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? squarer5 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer5=int64#10
+# asm 2: add <squarerax=%rax,<squarer5=%r12
+add %rax,%r12
+
+# qhasm: squarerax = squarer6
+# asm 1: mov <squarer6=int64#11,>squarerax=int64#7
+# asm 2: mov <squarer6=%r13,>squarerax=%rax
+mov %r13,%rax
+
+# qhasm: squarer6 = 0
+# asm 1: mov $0,>squarer6=int64#11
+# asm 2: mov $0,>squarer6=%r13
+mov $0,%r13
+
+# qhasm: squarer6 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer6=int64#11
+# asm 2: adc <squarerdx=%rdx,<squarer6=%r13
+adc %rdx,%r13
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? squarer6 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer6=int64#11
+# asm 2: add <squarerax=%rax,<squarer6=%r13
+add %rax,%r13
+
+# qhasm: squarerax = squarer7
+# asm 1: mov <squarer7=int64#4,>squarerax=int64#7
+# asm 2: mov <squarer7=%rcx,>squarerax=%rax
+mov %rcx,%rax
+
+# qhasm: squarer7 = 0
+# asm 1: mov $0,>squarer7=int64#4
+# asm 2: mov $0,>squarer7=%rcx
+mov $0,%rcx
+
+# qhasm: squarer7 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer7=int64#4
+# asm 2: adc <squarerdx=%rdx,<squarer7=%rcx
+adc %rdx,%rcx
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? squarer7 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer7=int64#4
+# asm 2: add <squarerax=%rax,<squarer7=%rcx
+add %rax,%rcx
+
+# qhasm: squarer8 = 0
+# asm 1: mov $0,>squarer8=int64#7
+# asm 2: mov $0,>squarer8=%rax
+mov $0,%rax
+
+# qhasm: squarer8 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer8=int64#7
+# asm 2: adc <squarerdx=%rdx,<squarer8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? b0 += squarer4
+# asm 1: add <squarer4=int64#9,<b0=int64#12
+# asm 2: add <squarer4=%r11,<b0=%r14
+add %r11,%r14
+
+# qhasm: carry? b1 += squarer5 + carry
+# asm 1: adc <squarer5=int64#10,<b1=int64#5
+# asm 2: adc <squarer5=%r12,<b1=%r8
+adc %r12,%r8
+
+# qhasm: carry? b2 += squarer6 + carry
+# asm 1: adc <squarer6=int64#11,<b2=int64#6
+# asm 2: adc <squarer6=%r13,<b2=%r9
+adc %r13,%r9
+
+# qhasm: carry? b3 += squarer7 + carry
+# asm 1: adc <squarer7=int64#4,<b3=int64#8
+# asm 2: adc <squarer7=%rcx,<b3=%r10
+adc %rcx,%r10
+
+# qhasm: squarezero = 0
+# asm 1: mov $0,>squarezero=int64#3
+# asm 2: mov $0,>squarezero=%rdx
+mov $0,%rdx
+
+# qhasm: squarer8 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<squarer8=int64#7
+# asm 2: adc <squarezero=%rdx,<squarer8=%rax
+adc %rdx,%rax
+
+# qhasm: squarer8 *= 38
+# asm 1: imulq $38,<squarer8=int64#7,>squarer8=int64#4
+# asm 2: imulq $38,<squarer8=%rax,>squarer8=%rcx
+imulq $38,%rax,%rcx
+
+# qhasm: carry? b0 += squarer8
+# asm 1: add <squarer8=int64#4,<b0=int64#12
+# asm 2: add <squarer8=%rcx,<b0=%r14
+add %rcx,%r14
+
+# qhasm: carry? b1 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<b1=int64#5
+# asm 2: adc <squarezero=%rdx,<b1=%r8
+adc %rdx,%r8
+
+# qhasm: carry? b2 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<b2=int64#6
+# asm 2: adc <squarezero=%rdx,<b2=%r9
+adc %rdx,%r9
+
+# qhasm: carry? b3 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<b3=int64#8
+# asm 2: adc <squarezero=%rdx,<b3=%r10
+adc %rdx,%r10
+
+# qhasm: squarezero += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<squarezero=int64#3
+# asm 2: adc <squarezero=%rdx,<squarezero=%rdx
+adc %rdx,%rdx
+
+# qhasm: squarezero *= 38
+# asm 1: imulq $38,<squarezero=int64#3,>squarezero=int64#3
+# asm 2: imulq $38,<squarezero=%rdx,>squarezero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: b0 += squarezero
+# asm 1: add <squarezero=int64#3,<b0=int64#12
+# asm 2: add <squarezero=%rdx,<b0=%r14
+add %rdx,%r14
+
+# qhasm: b0_stack = b0
+# asm 1: movq <b0=int64#12,>b0_stack=stack64#12
+# asm 2: movq <b0=%r14,>b0_stack=88(%rsp)
+movq %r14,88(%rsp)
+
+# qhasm: b1_stack = b1
+# asm 1: movq <b1=int64#5,>b1_stack=stack64#13
+# asm 2: movq <b1=%r8,>b1_stack=96(%rsp)
+movq %r8,96(%rsp)
+
+# qhasm: b2_stack = b2
+# asm 1: movq <b2=int64#6,>b2_stack=stack64#14
+# asm 2: movq <b2=%r9,>b2_stack=104(%rsp)
+movq %r9,104(%rsp)
+
+# qhasm: b3_stack = b3
+# asm 1: movq <b3=int64#8,>b3_stack=stack64#15
+# asm 2: movq <b3=%r10,>b3_stack=112(%rsp)
+movq %r10,112(%rsp)
+
+# qhasm: squarer7 = 0
+# asm 1: mov $0,>squarer7=int64#4
+# asm 2: mov $0,>squarer7=%rcx
+mov $0,%rcx
+
+# qhasm: squarerax = *(uint64 *)(pp + 72)
+# asm 1: movq 72(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 72(<pp=%rsi),>squarerax=%rax
+movq 72(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 64)
+# asm 1: mulq 64(<pp=int64#2)
+# asm 2: mulq 64(<pp=%rsi)
+mulq 64(%rsi)
+
+# qhasm: c1 = squarerax
+# asm 1: mov <squarerax=int64#7,>c1=int64#5
+# asm 2: mov <squarerax=%rax,>c1=%r8
+mov %rax,%r8
+
+# qhasm: c2 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>c2=int64#6
+# asm 2: mov <squarerdx=%rdx,>c2=%r9
+mov %rdx,%r9
+
+# qhasm: squarerax = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 80(<pp=%rsi),>squarerax=%rax
+movq 80(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 72)
+# asm 1: mulq 72(<pp=int64#2)
+# asm 2: mulq 72(<pp=%rsi)
+mulq 72(%rsi)
+
+# qhasm: c3 = squarerax
+# asm 1: mov <squarerax=int64#7,>c3=int64#8
+# asm 2: mov <squarerax=%rax,>c3=%r10
+mov %rax,%r10
+
+# qhasm: squarer4 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer4=int64#9
+# asm 2: mov <squarerdx=%rdx,>squarer4=%r11
+mov %rdx,%r11
+
+# qhasm: squarerax = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 88(<pp=%rsi),>squarerax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 80)
+# asm 1: mulq 80(<pp=int64#2)
+# asm 2: mulq 80(<pp=%rsi)
+mulq 80(%rsi)
+
+# qhasm: squarer5 = squarerax
+# asm 1: mov <squarerax=int64#7,>squarer5=int64#10
+# asm 2: mov <squarerax=%rax,>squarer5=%r12
+mov %rax,%r12
+
+# qhasm: squarer6 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer6=int64#11
+# asm 2: mov <squarerdx=%rdx,>squarer6=%r13
+mov %rdx,%r13
+
+# qhasm: squarerax = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 80(<pp=%rsi),>squarerax=%rax
+movq 80(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 64)
+# asm 1: mulq 64(<pp=int64#2)
+# asm 2: mulq 64(<pp=%rsi)
+mulq 64(%rsi)
+
+# qhasm: carry? c2 += squarerax
+# asm 1: add <squarerax=int64#7,<c2=int64#6
+# asm 2: add <squarerax=%rax,<c2=%r9
+add %rax,%r9
+
+# qhasm: carry? c3 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<c3=int64#8
+# asm 2: adc <squarerdx=%rdx,<c3=%r10
+adc %rdx,%r10
+
+# qhasm: squarer4 += 0 + carry
+# asm 1: adc $0,<squarer4=int64#9
+# asm 2: adc $0,<squarer4=%r11
+adc $0,%r11
+
+# qhasm: squarerax = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 88(<pp=%rsi),>squarerax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 72)
+# asm 1: mulq 72(<pp=int64#2)
+# asm 2: mulq 72(<pp=%rsi)
+mulq 72(%rsi)
+
+# qhasm: carry? squarer4 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer4=int64#9
+# asm 2: add <squarerax=%rax,<squarer4=%r11
+add %rax,%r11
+
+# qhasm: carry? squarer5 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer5=int64#10
+# asm 2: adc <squarerdx=%rdx,<squarer5=%r12
+adc %rdx,%r12
+
+# qhasm: squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarerax = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 88(<pp=%rsi),>squarerax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 64)
+# asm 1: mulq 64(<pp=int64#2)
+# asm 2: mulq 64(<pp=%rsi)
+mulq 64(%rsi)
+
+# qhasm: carry? c3 += squarerax
+# asm 1: add <squarerax=int64#7,<c3=int64#8
+# asm 2: add <squarerax=%rax,<c3=%r10
+add %rax,%r10
+
+# qhasm: carry? squarer4 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer4=int64#9
+# asm 2: adc <squarerdx=%rdx,<squarer4=%r11
+adc %rdx,%r11
+
+# qhasm: carry? squarer5 += 0 + carry
+# asm 1: adc $0,<squarer5=int64#10
+# asm 2: adc $0,<squarer5=%r12
+adc $0,%r12
+
+# qhasm: carry? squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarer7 += 0 + carry
+# asm 1: adc $0,<squarer7=int64#4
+# asm 2: adc $0,<squarer7=%rcx
+adc $0,%rcx
+
+# qhasm: carry? c1 += c1
+# asm 1: add <c1=int64#5,<c1=int64#5
+# asm 2: add <c1=%r8,<c1=%r8
+add %r8,%r8
+
+# qhasm: carry? c2 += c2 + carry
+# asm 1: adc <c2=int64#6,<c2=int64#6
+# asm 2: adc <c2=%r9,<c2=%r9
+adc %r9,%r9
+
+# qhasm: carry? c3 += c3 + carry
+# asm 1: adc <c3=int64#8,<c3=int64#8
+# asm 2: adc <c3=%r10,<c3=%r10
+adc %r10,%r10
+
+# qhasm: carry? squarer4 += squarer4 + carry
+# asm 1: adc <squarer4=int64#9,<squarer4=int64#9
+# asm 2: adc <squarer4=%r11,<squarer4=%r11
+adc %r11,%r11
+
+# qhasm: carry? squarer5 += squarer5 + carry
+# asm 1: adc <squarer5=int64#10,<squarer5=int64#10
+# asm 2: adc <squarer5=%r12,<squarer5=%r12
+adc %r12,%r12
+
+# qhasm: carry? squarer6 += squarer6 + carry
+# asm 1: adc <squarer6=int64#11,<squarer6=int64#11
+# asm 2: adc <squarer6=%r13,<squarer6=%r13
+adc %r13,%r13
+
+# qhasm: squarer7 += squarer7 + carry
+# asm 1: adc <squarer7=int64#4,<squarer7=int64#4
+# asm 2: adc <squarer7=%rcx,<squarer7=%rcx
+adc %rcx,%rcx
+
+# qhasm: squarerax = *(uint64 *)(pp + 64)
+# asm 1: movq 64(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 64(<pp=%rsi),>squarerax=%rax
+movq 64(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 64)
+# asm 1: mulq 64(<pp=int64#2)
+# asm 2: mulq 64(<pp=%rsi)
+mulq 64(%rsi)
+
+# qhasm: c0 = squarerax
+# asm 1: mov <squarerax=int64#7,>c0=int64#12
+# asm 2: mov <squarerax=%rax,>c0=%r14
+mov %rax,%r14
+
+# qhasm: squaret1 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squaret1=int64#13
+# asm 2: mov <squarerdx=%rdx,>squaret1=%r15
+mov %rdx,%r15
+
+# qhasm: squarerax = *(uint64 *)(pp + 72)
+# asm 1: movq 72(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 72(<pp=%rsi),>squarerax=%rax
+movq 72(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 72)
+# asm 1: mulq 72(<pp=int64#2)
+# asm 2: mulq 72(<pp=%rsi)
+mulq 72(%rsi)
+
+# qhasm: squaret2 = squarerax
+# asm 1: mov <squarerax=int64#7,>squaret2=int64#14
+# asm 2: mov <squarerax=%rax,>squaret2=%rbx
+mov %rax,%rbx
+
+# qhasm: squaret3 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squaret3=int64#15
+# asm 2: mov <squarerdx=%rdx,>squaret3=%rbp
+mov %rdx,%rbp
+
+# qhasm: squarerax = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 80(<pp=%rsi),>squarerax=%rax
+movq 80(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 80)
+# asm 1: mulq 80(<pp=int64#2)
+# asm 2: mulq 80(<pp=%rsi)
+mulq 80(%rsi)
+
+# qhasm: carry? c1 += squaret1
+# asm 1: add <squaret1=int64#13,<c1=int64#5
+# asm 2: add <squaret1=%r15,<c1=%r8
+add %r15,%r8
+
+# qhasm: carry? c2 += squaret2 + carry
+# asm 1: adc <squaret2=int64#14,<c2=int64#6
+# asm 2: adc <squaret2=%rbx,<c2=%r9
+adc %rbx,%r9
+
+# qhasm: carry? c3 += squaret3 + carry
+# asm 1: adc <squaret3=int64#15,<c3=int64#8
+# asm 2: adc <squaret3=%rbp,<c3=%r10
+adc %rbp,%r10
+
+# qhasm: carry? squarer4 += squarerax + carry
+# asm 1: adc <squarerax=int64#7,<squarer4=int64#9
+# asm 2: adc <squarerax=%rax,<squarer4=%r11
+adc %rax,%r11
+
+# qhasm: carry? squarer5 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer5=int64#10
+# asm 2: adc <squarerdx=%rdx,<squarer5=%r12
+adc %rdx,%r12
+
+# qhasm: carry? squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarer7 += 0 + carry
+# asm 1: adc $0,<squarer7=int64#4
+# asm 2: adc $0,<squarer7=%rcx
+adc $0,%rcx
+
+# qhasm: squarerax = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 88(<pp=%rsi),>squarerax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 88)
+# asm 1: mulq 88(<pp=int64#2)
+# asm 2: mulq 88(<pp=%rsi)
+mulq 88(%rsi)
+
+# qhasm: carry? squarer6 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer6=int64#11
+# asm 2: add <squarerax=%rax,<squarer6=%r13
+add %rax,%r13
+
+# qhasm: squarer7 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer7=int64#4
+# asm 2: adc <squarerdx=%rdx,<squarer7=%rcx
+adc %rdx,%rcx
+
+# qhasm: squarerax = squarer4
+# asm 1: mov <squarer4=int64#9,>squarerax=int64#7
+# asm 2: mov <squarer4=%r11,>squarerax=%rax
+mov %r11,%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: squarer4 = squarerax
+# asm 1: mov <squarerax=int64#7,>squarer4=int64#9
+# asm 2: mov <squarerax=%rax,>squarer4=%r11
+mov %rax,%r11
+
+# qhasm: squarerax = squarer5
+# asm 1: mov <squarer5=int64#10,>squarerax=int64#7
+# asm 2: mov <squarer5=%r12,>squarerax=%rax
+mov %r12,%rax
+
+# qhasm: squarer5 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer5=int64#10
+# asm 2: mov <squarerdx=%rdx,>squarer5=%r12
+mov %rdx,%r12
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? squarer5 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer5=int64#10
+# asm 2: add <squarerax=%rax,<squarer5=%r12
+add %rax,%r12
+
+# qhasm: squarerax = squarer6
+# asm 1: mov <squarer6=int64#11,>squarerax=int64#7
+# asm 2: mov <squarer6=%r13,>squarerax=%rax
+mov %r13,%rax
+
+# qhasm: squarer6 = 0
+# asm 1: mov $0,>squarer6=int64#11
+# asm 2: mov $0,>squarer6=%r13
+mov $0,%r13
+
+# qhasm: squarer6 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer6=int64#11
+# asm 2: adc <squarerdx=%rdx,<squarer6=%r13
+adc %rdx,%r13
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? squarer6 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer6=int64#11
+# asm 2: add <squarerax=%rax,<squarer6=%r13
+add %rax,%r13
+
+# qhasm: squarerax = squarer7
+# asm 1: mov <squarer7=int64#4,>squarerax=int64#7
+# asm 2: mov <squarer7=%rcx,>squarerax=%rax
+mov %rcx,%rax
+
+# qhasm: squarer7 = 0
+# asm 1: mov $0,>squarer7=int64#4
+# asm 2: mov $0,>squarer7=%rcx
+mov $0,%rcx
+
+# qhasm: squarer7 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer7=int64#4
+# asm 2: adc <squarerdx=%rdx,<squarer7=%rcx
+adc %rdx,%rcx
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? squarer7 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer7=int64#4
+# asm 2: add <squarerax=%rax,<squarer7=%rcx
+add %rax,%rcx
+
+# qhasm: squarer8 = 0
+# asm 1: mov $0,>squarer8=int64#7
+# asm 2: mov $0,>squarer8=%rax
+mov $0,%rax
+
+# qhasm: squarer8 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer8=int64#7
+# asm 2: adc <squarerdx=%rdx,<squarer8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? c0 += squarer4
+# asm 1: add <squarer4=int64#9,<c0=int64#12
+# asm 2: add <squarer4=%r11,<c0=%r14
+add %r11,%r14
+
+# qhasm: carry? c1 += squarer5 + carry
+# asm 1: adc <squarer5=int64#10,<c1=int64#5
+# asm 2: adc <squarer5=%r12,<c1=%r8
+adc %r12,%r8
+
+# qhasm: carry? c2 += squarer6 + carry
+# asm 1: adc <squarer6=int64#11,<c2=int64#6
+# asm 2: adc <squarer6=%r13,<c2=%r9
+adc %r13,%r9
+
+# qhasm: carry? c3 += squarer7 + carry
+# asm 1: adc <squarer7=int64#4,<c3=int64#8
+# asm 2: adc <squarer7=%rcx,<c3=%r10
+adc %rcx,%r10
+
+# qhasm: squarezero = 0
+# asm 1: mov $0,>squarezero=int64#3
+# asm 2: mov $0,>squarezero=%rdx
+mov $0,%rdx
+
+# qhasm: squarer8 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<squarer8=int64#7
+# asm 2: adc <squarezero=%rdx,<squarer8=%rax
+adc %rdx,%rax
+
+# qhasm: squarer8 *= 38
+# asm 1: imulq $38,<squarer8=int64#7,>squarer8=int64#4
+# asm 2: imulq $38,<squarer8=%rax,>squarer8=%rcx
+imulq $38,%rax,%rcx
+
+# qhasm: carry? c0 += squarer8
+# asm 1: add <squarer8=int64#4,<c0=int64#12
+# asm 2: add <squarer8=%rcx,<c0=%r14
+add %rcx,%r14
+
+# qhasm: carry? c1 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<c1=int64#5
+# asm 2: adc <squarezero=%rdx,<c1=%r8
+adc %rdx,%r8
+
+# qhasm: carry? c2 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<c2=int64#6
+# asm 2: adc <squarezero=%rdx,<c2=%r9
+adc %rdx,%r9
+
+# qhasm: carry? c3 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<c3=int64#8
+# asm 2: adc <squarezero=%rdx,<c3=%r10
+adc %rdx,%r10
+
+# qhasm: squarezero += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<squarezero=int64#3
+# asm 2: adc <squarezero=%rdx,<squarezero=%rdx
+adc %rdx,%rdx
+
+# qhasm: squarezero *= 38
+# asm 1: imulq $38,<squarezero=int64#3,>squarezero=int64#3
+# asm 2: imulq $38,<squarezero=%rdx,>squarezero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: c0 += squarezero
+# asm 1: add <squarezero=int64#3,<c0=int64#12
+# asm 2: add <squarezero=%rdx,<c0=%r14
+add %rdx,%r14
+
+# qhasm: carry? c0 += c0
+# asm 1: add <c0=int64#12,<c0=int64#12
+# asm 2: add <c0=%r14,<c0=%r14
+add %r14,%r14
+
+# qhasm: carry? c1 += c1 + carry
+# asm 1: adc <c1=int64#5,<c1=int64#5
+# asm 2: adc <c1=%r8,<c1=%r8
+adc %r8,%r8
+
+# qhasm: carry? c2 += c2 + carry
+# asm 1: adc <c2=int64#6,<c2=int64#6
+# asm 2: adc <c2=%r9,<c2=%r9
+adc %r9,%r9
+
+# qhasm: carry? c3 += c3 + carry
+# asm 1: adc <c3=int64#8,<c3=int64#8
+# asm 2: adc <c3=%r10,<c3=%r10
+adc %r10,%r10
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#3
+# asm 2: mov $0,>addt0=%rdx
+mov $0,%rdx
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#4
+# asm 2: mov $38,>addt1=%rcx
+mov $38,%rcx
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#3,<addt1=int64#4
+# asm 2: cmovae <addt0=%rdx,<addt1=%rcx
+cmovae %rdx,%rcx
+
+# qhasm: carry? c0 += addt1
+# asm 1: add <addt1=int64#4,<c0=int64#12
+# asm 2: add <addt1=%rcx,<c0=%r14
+add %rcx,%r14
+
+# qhasm: carry? c1 += addt0 + carry
+# asm 1: adc <addt0=int64#3,<c1=int64#5
+# asm 2: adc <addt0=%rdx,<c1=%r8
+adc %rdx,%r8
+
+# qhasm: carry? c2 += addt0 + carry
+# asm 1: adc <addt0=int64#3,<c2=int64#6
+# asm 2: adc <addt0=%rdx,<c2=%r9
+adc %rdx,%r9
+
+# qhasm: carry? c3 += addt0 + carry
+# asm 1: adc <addt0=int64#3,<c3=int64#8
+# asm 2: adc <addt0=%rdx,<c3=%r10
+adc %rdx,%r10
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#4,<addt0=int64#3
+# asm 2: cmovc <addt1=%rcx,<addt0=%rdx
+cmovc %rcx,%rdx
+
+# qhasm: c0 += addt0
+# asm 1: add <addt0=int64#3,<c0=int64#12
+# asm 2: add <addt0=%rdx,<c0=%r14
+add %rdx,%r14
+
+# qhasm: c0_stack = c0
+# asm 1: movq <c0=int64#12,>c0_stack=stack64#16
+# asm 2: movq <c0=%r14,>c0_stack=120(%rsp)
+movq %r14,120(%rsp)
+
+# qhasm: c1_stack = c1
+# asm 1: movq <c1=int64#5,>c1_stack=stack64#17
+# asm 2: movq <c1=%r8,>c1_stack=128(%rsp)
+movq %r8,128(%rsp)
+
+# qhasm: c2_stack = c2
+# asm 1: movq <c2=int64#6,>c2_stack=stack64#18
+# asm 2: movq <c2=%r9,>c2_stack=136(%rsp)
+movq %r9,136(%rsp)
+
+# qhasm: c3_stack = c3
+# asm 1: movq <c3=int64#8,>c3_stack=stack64#19
+# asm 2: movq <c3=%r10,>c3_stack=144(%rsp)
+movq %r10,144(%rsp)
+
+# qhasm: d0 = 0
+# asm 1: mov $0,>d0=int64#3
+# asm 2: mov $0,>d0=%rdx
+mov $0,%rdx
+
+# qhasm: d1 = 0
+# asm 1: mov $0,>d1=int64#4
+# asm 2: mov $0,>d1=%rcx
+mov $0,%rcx
+
+# qhasm: d2 = 0
+# asm 1: mov $0,>d2=int64#5
+# asm 2: mov $0,>d2=%r8
+mov $0,%r8
+
+# qhasm: d3 = 0
+# asm 1: mov $0,>d3=int64#6
+# asm 2: mov $0,>d3=%r9
+mov $0,%r9
+
+# qhasm: carry? d0 -= a0_stack
+# asm 1: subq <a0_stack=stack64#8,<d0=int64#3
+# asm 2: subq <a0_stack=56(%rsp),<d0=%rdx
+subq 56(%rsp),%rdx
+
+# qhasm: carry? d1 -= a1_stack - carry
+# asm 1: sbbq <a1_stack=stack64#9,<d1=int64#4
+# asm 2: sbbq <a1_stack=64(%rsp),<d1=%rcx
+sbbq 64(%rsp),%rcx
+
+# qhasm: carry? d2 -= a2_stack - carry
+# asm 1: sbbq <a2_stack=stack64#10,<d2=int64#5
+# asm 2: sbbq <a2_stack=72(%rsp),<d2=%r8
+sbbq 72(%rsp),%r8
+
+# qhasm: carry? d3 -= a3_stack - carry
+# asm 1: sbbq <a3_stack=stack64#11,<d3=int64#6
+# asm 2: sbbq <a3_stack=80(%rsp),<d3=%r9
+sbbq 80(%rsp),%r9
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#7
+# asm 2: mov $0,>subt0=%rax
+mov $0,%rax
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#8
+# asm 2: mov $38,>subt1=%r10
+mov $38,%r10
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#7,<subt1=int64#8
+# asm 2: cmovae <subt0=%rax,<subt1=%r10
+cmovae %rax,%r10
+
+# qhasm: carry? d0 -= subt1
+# asm 1: sub <subt1=int64#8,<d0=int64#3
+# asm 2: sub <subt1=%r10,<d0=%rdx
+sub %r10,%rdx
+
+# qhasm: carry? d1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#7,<d1=int64#4
+# asm 2: sbb <subt0=%rax,<d1=%rcx
+sbb %rax,%rcx
+
+# qhasm: carry? d2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#7,<d2=int64#5
+# asm 2: sbb <subt0=%rax,<d2=%r8
+sbb %rax,%r8
+
+# qhasm: carry? d3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#7,<d3=int64#6
+# asm 2: sbb <subt0=%rax,<d3=%r9
+sbb %rax,%r9
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#8,<subt0=int64#7
+# asm 2: cmovc <subt1=%r10,<subt0=%rax
+cmovc %r10,%rax
+
+# qhasm: d0 -= subt0
+# asm 1: sub <subt0=int64#7,<d0=int64#3
+# asm 2: sub <subt0=%rax,<d0=%rdx
+sub %rax,%rdx
+
+# qhasm: d0_stack = d0
+# asm 1: movq <d0=int64#3,>d0_stack=stack64#8
+# asm 2: movq <d0=%rdx,>d0_stack=56(%rsp)
+movq %rdx,56(%rsp)
+
+# qhasm: d1_stack = d1
+# asm 1: movq <d1=int64#4,>d1_stack=stack64#9
+# asm 2: movq <d1=%rcx,>d1_stack=64(%rsp)
+movq %rcx,64(%rsp)
+
+# qhasm: d2_stack = d2
+# asm 1: movq <d2=int64#5,>d2_stack=stack64#10
+# asm 2: movq <d2=%r8,>d2_stack=72(%rsp)
+movq %r8,72(%rsp)
+
+# qhasm: d3_stack = d3
+# asm 1: movq <d3=int64#6,>d3_stack=stack64#11
+# asm 2: movq <d3=%r9,>d3_stack=80(%rsp)
+movq %r9,80(%rsp)
+
+# qhasm: e0 = 0
+# asm 1: mov $0,>e0=int64#7
+# asm 2: mov $0,>e0=%rax
+mov $0,%rax
+
+# qhasm: e1 = 0
+# asm 1: mov $0,>e1=int64#8
+# asm 2: mov $0,>e1=%r10
+mov $0,%r10
+
+# qhasm: e2 = 0
+# asm 1: mov $0,>e2=int64#9
+# asm 2: mov $0,>e2=%r11
+mov $0,%r11
+
+# qhasm: e3 = 0
+# asm 1: mov $0,>e3=int64#10
+# asm 2: mov $0,>e3=%r12
+mov $0,%r12
+
+# qhasm: carry? e0 -= b0_stack
+# asm 1: subq <b0_stack=stack64#12,<e0=int64#7
+# asm 2: subq <b0_stack=88(%rsp),<e0=%rax
+subq 88(%rsp),%rax
+
+# qhasm: carry? e1 -= b1_stack - carry
+# asm 1: sbbq <b1_stack=stack64#13,<e1=int64#8
+# asm 2: sbbq <b1_stack=96(%rsp),<e1=%r10
+sbbq 96(%rsp),%r10
+
+# qhasm: carry? e2 -= b2_stack - carry
+# asm 1: sbbq <b2_stack=stack64#14,<e2=int64#9
+# asm 2: sbbq <b2_stack=104(%rsp),<e2=%r11
+sbbq 104(%rsp),%r11
+
+# qhasm: carry? e3 -= b3_stack - carry
+# asm 1: sbbq <b3_stack=stack64#15,<e3=int64#10
+# asm 2: sbbq <b3_stack=112(%rsp),<e3=%r12
+sbbq 112(%rsp),%r12
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#11
+# asm 2: mov $0,>subt0=%r13
+mov $0,%r13
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#12
+# asm 2: mov $38,>subt1=%r14
+mov $38,%r14
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#11,<subt1=int64#12
+# asm 2: cmovae <subt0=%r13,<subt1=%r14
+cmovae %r13,%r14
+
+# qhasm: carry? e0 -= subt1
+# asm 1: sub <subt1=int64#12,<e0=int64#7
+# asm 2: sub <subt1=%r14,<e0=%rax
+sub %r14,%rax
+
+# qhasm: carry? e1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#11,<e1=int64#8
+# asm 2: sbb <subt0=%r13,<e1=%r10
+sbb %r13,%r10
+
+# qhasm: carry? e2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#11,<e2=int64#9
+# asm 2: sbb <subt0=%r13,<e2=%r11
+sbb %r13,%r11
+
+# qhasm: carry? e3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#11,<e3=int64#10
+# asm 2: sbb <subt0=%r13,<e3=%r12
+sbb %r13,%r12
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#12,<subt0=int64#11
+# asm 2: cmovc <subt1=%r14,<subt0=%r13
+cmovc %r14,%r13
+
+# qhasm: e0 -= subt0
+# asm 1: sub <subt0=int64#11,<e0=int64#7
+# asm 2: sub <subt0=%r13,<e0=%rax
+sub %r13,%rax
+
+# qhasm: e0_stack = e0
+# asm 1: movq <e0=int64#7,>e0_stack=stack64#20
+# asm 2: movq <e0=%rax,>e0_stack=152(%rsp)
+movq %rax,152(%rsp)
+
+# qhasm: e1_stack = e1
+# asm 1: movq <e1=int64#8,>e1_stack=stack64#21
+# asm 2: movq <e1=%r10,>e1_stack=160(%rsp)
+movq %r10,160(%rsp)
+
+# qhasm: e2_stack = e2
+# asm 1: movq <e2=int64#9,>e2_stack=stack64#22
+# asm 2: movq <e2=%r11,>e2_stack=168(%rsp)
+movq %r11,168(%rsp)
+
+# qhasm: e3_stack = e3
+# asm 1: movq <e3=int64#10,>e3_stack=stack64#23
+# asm 2: movq <e3=%r12,>e3_stack=176(%rsp)
+movq %r12,176(%rsp)
+
+# qhasm: rz0 = d0
+# asm 1: mov <d0=int64#3,>rz0=int64#7
+# asm 2: mov <d0=%rdx,>rz0=%rax
+mov %rdx,%rax
+
+# qhasm: rz1 = d1
+# asm 1: mov <d1=int64#4,>rz1=int64#8
+# asm 2: mov <d1=%rcx,>rz1=%r10
+mov %rcx,%r10
+
+# qhasm: rz2 = d2
+# asm 1: mov <d2=int64#5,>rz2=int64#9
+# asm 2: mov <d2=%r8,>rz2=%r11
+mov %r8,%r11
+
+# qhasm: rz3 = d3
+# asm 1: mov <d3=int64#6,>rz3=int64#10
+# asm 2: mov <d3=%r9,>rz3=%r12
+mov %r9,%r12
+
+# qhasm: carry? rz0 += b0_stack
+# asm 1: addq <b0_stack=stack64#12,<rz0=int64#7
+# asm 2: addq <b0_stack=88(%rsp),<rz0=%rax
+addq 88(%rsp),%rax
+
+# qhasm: carry? rz1 += b1_stack + carry
+# asm 1: adcq <b1_stack=stack64#13,<rz1=int64#8
+# asm 2: adcq <b1_stack=96(%rsp),<rz1=%r10
+adcq 96(%rsp),%r10
+
+# qhasm: carry? rz2 += b2_stack + carry
+# asm 1: adcq <b2_stack=stack64#14,<rz2=int64#9
+# asm 2: adcq <b2_stack=104(%rsp),<rz2=%r11
+adcq 104(%rsp),%r11
+
+# qhasm: carry? rz3 += b3_stack + carry
+# asm 1: adcq <b3_stack=stack64#15,<rz3=int64#10
+# asm 2: adcq <b3_stack=112(%rsp),<rz3=%r12
+adcq 112(%rsp),%r12
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#11
+# asm 2: mov $0,>addt0=%r13
+mov $0,%r13
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#12
+# asm 2: mov $38,>addt1=%r14
+mov $38,%r14
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#11,<addt1=int64#12
+# asm 2: cmovae <addt0=%r13,<addt1=%r14
+cmovae %r13,%r14
+
+# qhasm: carry? rz0 += addt1
+# asm 1: add <addt1=int64#12,<rz0=int64#7
+# asm 2: add <addt1=%r14,<rz0=%rax
+add %r14,%rax
+
+# qhasm: carry? rz1 += addt0 + carry
+# asm 1: adc <addt0=int64#11,<rz1=int64#8
+# asm 2: adc <addt0=%r13,<rz1=%r10
+adc %r13,%r10
+
+# qhasm: carry? rz2 += addt0 + carry
+# asm 1: adc <addt0=int64#11,<rz2=int64#9
+# asm 2: adc <addt0=%r13,<rz2=%r11
+adc %r13,%r11
+
+# qhasm: carry? rz3 += addt0 + carry
+# asm 1: adc <addt0=int64#11,<rz3=int64#10
+# asm 2: adc <addt0=%r13,<rz3=%r12
+adc %r13,%r12
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#12,<addt0=int64#11
+# asm 2: cmovc <addt1=%r14,<addt0=%r13
+cmovc %r14,%r13
+
+# qhasm: rz0 += addt0
+# asm 1: add <addt0=int64#11,<rz0=int64#7
+# asm 2: add <addt0=%r13,<rz0=%rax
+add %r13,%rax
+
+# qhasm: *(uint64 *) (rp + 32) = rz0
+# asm 1: movq <rz0=int64#7,32(<rp=int64#1)
+# asm 2: movq <rz0=%rax,32(<rp=%rdi)
+movq %rax,32(%rdi)
+
+# qhasm: *(uint64 *) (rp + 40) = rz1
+# asm 1: movq <rz1=int64#8,40(<rp=int64#1)
+# asm 2: movq <rz1=%r10,40(<rp=%rdi)
+movq %r10,40(%rdi)
+
+# qhasm: *(uint64 *) (rp + 48) = rz2
+# asm 1: movq <rz2=int64#9,48(<rp=int64#1)
+# asm 2: movq <rz2=%r11,48(<rp=%rdi)
+movq %r11,48(%rdi)
+
+# qhasm: *(uint64 *) (rp + 56) = rz3
+# asm 1: movq <rz3=int64#10,56(<rp=int64#1)
+# asm 2: movq <rz3=%r12,56(<rp=%rdi)
+movq %r12,56(%rdi)
+
+# qhasm: carry? d0 -= b0_stack
+# asm 1: subq <b0_stack=stack64#12,<d0=int64#3
+# asm 2: subq <b0_stack=88(%rsp),<d0=%rdx
+subq 88(%rsp),%rdx
+
+# qhasm: carry? d1 -= b1_stack - carry
+# asm 1: sbbq <b1_stack=stack64#13,<d1=int64#4
+# asm 2: sbbq <b1_stack=96(%rsp),<d1=%rcx
+sbbq 96(%rsp),%rcx
+
+# qhasm: carry? d2 -= b2_stack - carry
+# asm 1: sbbq <b2_stack=stack64#14,<d2=int64#5
+# asm 2: sbbq <b2_stack=104(%rsp),<d2=%r8
+sbbq 104(%rsp),%r8
+
+# qhasm: carry? d3 -= b3_stack - carry
+# asm 1: sbbq <b3_stack=stack64#15,<d3=int64#6
+# asm 2: sbbq <b3_stack=112(%rsp),<d3=%r9
+sbbq 112(%rsp),%r9
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#11
+# asm 2: mov $0,>subt0=%r13
+mov $0,%r13
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#12
+# asm 2: mov $38,>subt1=%r14
+mov $38,%r14
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#11,<subt1=int64#12
+# asm 2: cmovae <subt0=%r13,<subt1=%r14
+cmovae %r13,%r14
+
+# qhasm: carry? d0 -= subt1
+# asm 1: sub <subt1=int64#12,<d0=int64#3
+# asm 2: sub <subt1=%r14,<d0=%rdx
+sub %r14,%rdx
+
+# qhasm: carry? d1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#11,<d1=int64#4
+# asm 2: sbb <subt0=%r13,<d1=%rcx
+sbb %r13,%rcx
+
+# qhasm: carry? d2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#11,<d2=int64#5
+# asm 2: sbb <subt0=%r13,<d2=%r8
+sbb %r13,%r8
+
+# qhasm: carry? d3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#11,<d3=int64#6
+# asm 2: sbb <subt0=%r13,<d3=%r9
+sbb %r13,%r9
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#12,<subt0=int64#11
+# asm 2: cmovc <subt1=%r14,<subt0=%r13
+cmovc %r14,%r13
+
+# qhasm: d0 -= subt0
+# asm 1: sub <subt0=int64#11,<d0=int64#3
+# asm 2: sub <subt0=%r13,<d0=%rdx
+sub %r13,%rdx
+
+# qhasm: *(uint64 *)(rp + 64) = d0
+# asm 1: movq <d0=int64#3,64(<rp=int64#1)
+# asm 2: movq <d0=%rdx,64(<rp=%rdi)
+movq %rdx,64(%rdi)
+
+# qhasm: *(uint64 *)(rp + 72) = d1
+# asm 1: movq <d1=int64#4,72(<rp=int64#1)
+# asm 2: movq <d1=%rcx,72(<rp=%rdi)
+movq %rcx,72(%rdi)
+
+# qhasm: *(uint64 *)(rp + 80) = d2
+# asm 1: movq <d2=int64#5,80(<rp=int64#1)
+# asm 2: movq <d2=%r8,80(<rp=%rdi)
+movq %r8,80(%rdi)
+
+# qhasm: *(uint64 *)(rp + 88) = d3
+# asm 1: movq <d3=int64#6,88(<rp=int64#1)
+# asm 2: movq <d3=%r9,88(<rp=%rdi)
+movq %r9,88(%rdi)
+
+# qhasm: carry? rz0 -= c0_stack
+# asm 1: subq <c0_stack=stack64#16,<rz0=int64#7
+# asm 2: subq <c0_stack=120(%rsp),<rz0=%rax
+subq 120(%rsp),%rax
+
+# qhasm: carry? rz1 -= c1_stack - carry
+# asm 1: sbbq <c1_stack=stack64#17,<rz1=int64#8
+# asm 2: sbbq <c1_stack=128(%rsp),<rz1=%r10
+sbbq 128(%rsp),%r10
+
+# qhasm: carry? rz2 -= c2_stack - carry
+# asm 1: sbbq <c2_stack=stack64#18,<rz2=int64#9
+# asm 2: sbbq <c2_stack=136(%rsp),<rz2=%r11
+sbbq 136(%rsp),%r11
+
+# qhasm: carry? rz3 -= c3_stack - carry
+# asm 1: sbbq <c3_stack=stack64#19,<rz3=int64#10
+# asm 2: sbbq <c3_stack=144(%rsp),<rz3=%r12
+sbbq 144(%rsp),%r12
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#3
+# asm 2: mov $0,>subt0=%rdx
+mov $0,%rdx
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#4
+# asm 2: mov $38,>subt1=%rcx
+mov $38,%rcx
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#3,<subt1=int64#4
+# asm 2: cmovae <subt0=%rdx,<subt1=%rcx
+cmovae %rdx,%rcx
+
+# qhasm: carry? rz0 -= subt1
+# asm 1: sub <subt1=int64#4,<rz0=int64#7
+# asm 2: sub <subt1=%rcx,<rz0=%rax
+sub %rcx,%rax
+
+# qhasm: carry? rz1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#3,<rz1=int64#8
+# asm 2: sbb <subt0=%rdx,<rz1=%r10
+sbb %rdx,%r10
+
+# qhasm: carry? rz2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#3,<rz2=int64#9
+# asm 2: sbb <subt0=%rdx,<rz2=%r11
+sbb %rdx,%r11
+
+# qhasm: carry? rz3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#3,<rz3=int64#10
+# asm 2: sbb <subt0=%rdx,<rz3=%r12
+sbb %rdx,%r12
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#4,<subt0=int64#3
+# asm 2: cmovc <subt1=%rcx,<subt0=%rdx
+cmovc %rcx,%rdx
+
+# qhasm: rz0 -= subt0
+# asm 1: sub <subt0=int64#3,<rz0=int64#7
+# asm 2: sub <subt0=%rdx,<rz0=%rax
+sub %rdx,%rax
+
+# qhasm: *(uint64 *) (rp + 96) = rz0
+# asm 1: movq <rz0=int64#7,96(<rp=int64#1)
+# asm 2: movq <rz0=%rax,96(<rp=%rdi)
+movq %rax,96(%rdi)
+
+# qhasm: *(uint64 *) (rp + 104) = rz1
+# asm 1: movq <rz1=int64#8,104(<rp=int64#1)
+# asm 2: movq <rz1=%r10,104(<rp=%rdi)
+movq %r10,104(%rdi)
+
+# qhasm: *(uint64 *) (rp + 112) = rz2
+# asm 1: movq <rz2=int64#9,112(<rp=int64#1)
+# asm 2: movq <rz2=%r11,112(<rp=%rdi)
+movq %r11,112(%rdi)
+
+# qhasm: *(uint64 *) (rp + 120) = rz3
+# asm 1: movq <rz3=int64#10,120(<rp=int64#1)
+# asm 2: movq <rz3=%r12,120(<rp=%rdi)
+movq %r12,120(%rdi)
+
+# qhasm: rx0 = *(uint64 *)(pp + 0)
+# asm 1: movq 0(<pp=int64#2),>rx0=int64#3
+# asm 2: movq 0(<pp=%rsi),>rx0=%rdx
+movq 0(%rsi),%rdx
+
+# qhasm: rx1 = *(uint64 *)(pp + 8)
+# asm 1: movq 8(<pp=int64#2),>rx1=int64#4
+# asm 2: movq 8(<pp=%rsi),>rx1=%rcx
+movq 8(%rsi),%rcx
+
+# qhasm: rx2 = *(uint64 *)(pp + 16)
+# asm 1: movq 16(<pp=int64#2),>rx2=int64#5
+# asm 2: movq 16(<pp=%rsi),>rx2=%r8
+movq 16(%rsi),%r8
+
+# qhasm: rx3 = *(uint64 *)(pp + 24)
+# asm 1: movq 24(<pp=int64#2),>rx3=int64#6
+# asm 2: movq 24(<pp=%rsi),>rx3=%r9
+movq 24(%rsi),%r9
+
+# qhasm: carry? rx0 += *(uint64 *)(pp + 32)
+# asm 1: addq 32(<pp=int64#2),<rx0=int64#3
+# asm 2: addq 32(<pp=%rsi),<rx0=%rdx
+addq 32(%rsi),%rdx
+
+# qhasm: carry? rx1 += *(uint64 *)(pp + 40) + carry
+# asm 1: adcq 40(<pp=int64#2),<rx1=int64#4
+# asm 2: adcq 40(<pp=%rsi),<rx1=%rcx
+adcq 40(%rsi),%rcx
+
+# qhasm: carry? rx2 += *(uint64 *)(pp + 48) + carry
+# asm 1: adcq 48(<pp=int64#2),<rx2=int64#5
+# asm 2: adcq 48(<pp=%rsi),<rx2=%r8
+adcq 48(%rsi),%r8
+
+# qhasm: carry? rx3 += *(uint64 *)(pp + 56) + carry
+# asm 1: adcq 56(<pp=int64#2),<rx3=int64#6
+# asm 2: adcq 56(<pp=%rsi),<rx3=%r9
+adcq 56(%rsi),%r9
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#2
+# asm 2: mov $0,>addt0=%rsi
+mov $0,%rsi
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#7
+# asm 2: mov $38,>addt1=%rax
+mov $38,%rax
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#2,<addt1=int64#7
+# asm 2: cmovae <addt0=%rsi,<addt1=%rax
+cmovae %rsi,%rax
+
+# qhasm: carry? rx0 += addt1
+# asm 1: add <addt1=int64#7,<rx0=int64#3
+# asm 2: add <addt1=%rax,<rx0=%rdx
+add %rax,%rdx
+
+# qhasm: carry? rx1 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rx1=int64#4
+# asm 2: adc <addt0=%rsi,<rx1=%rcx
+adc %rsi,%rcx
+
+# qhasm: carry? rx2 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rx2=int64#5
+# asm 2: adc <addt0=%rsi,<rx2=%r8
+adc %rsi,%r8
+
+# qhasm: carry? rx3 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rx3=int64#6
+# asm 2: adc <addt0=%rsi,<rx3=%r9
+adc %rsi,%r9
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#7,<addt0=int64#2
+# asm 2: cmovc <addt1=%rax,<addt0=%rsi
+cmovc %rax,%rsi
+
+# qhasm: rx0 += addt0
+# asm 1: add <addt0=int64#2,<rx0=int64#3
+# asm 2: add <addt0=%rsi,<rx0=%rdx
+add %rsi,%rdx
+
+# qhasm: rx0_stack = rx0
+# asm 1: movq <rx0=int64#3,>rx0_stack=stack64#12
+# asm 2: movq <rx0=%rdx,>rx0_stack=88(%rsp)
+movq %rdx,88(%rsp)
+
+# qhasm: rx1_stack = rx1
+# asm 1: movq <rx1=int64#4,>rx1_stack=stack64#13
+# asm 2: movq <rx1=%rcx,>rx1_stack=96(%rsp)
+movq %rcx,96(%rsp)
+
+# qhasm: rx2_stack = rx2
+# asm 1: movq <rx2=int64#5,>rx2_stack=stack64#14
+# asm 2: movq <rx2=%r8,>rx2_stack=104(%rsp)
+movq %r8,104(%rsp)
+
+# qhasm: rx3_stack = rx3
+# asm 1: movq <rx3=int64#6,>rx3_stack=stack64#15
+# asm 2: movq <rx3=%r9,>rx3_stack=112(%rsp)
+movq %r9,112(%rsp)
+
+# qhasm: squarer7 = 0
+# asm 1: mov $0,>squarer7=int64#2
+# asm 2: mov $0,>squarer7=%rsi
+mov $0,%rsi
+
+# qhasm: squarerax = rx1_stack
+# asm 1: movq <rx1_stack=stack64#13,>squarerax=int64#7
+# asm 2: movq <rx1_stack=96(%rsp),>squarerax=%rax
+movq 96(%rsp),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * rx0_stack
+# asm 1: mulq <rx0_stack=stack64#12
+# asm 2: mulq <rx0_stack=88(%rsp)
+mulq 88(%rsp)
+
+# qhasm: rx1 = squarerax
+# asm 1: mov <squarerax=int64#7,>rx1=int64#4
+# asm 2: mov <squarerax=%rax,>rx1=%rcx
+mov %rax,%rcx
+
+# qhasm: rx2 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>rx2=int64#5
+# asm 2: mov <squarerdx=%rdx,>rx2=%r8
+mov %rdx,%r8
+
+# qhasm: squarerax = rx2_stack
+# asm 1: movq <rx2_stack=stack64#14,>squarerax=int64#7
+# asm 2: movq <rx2_stack=104(%rsp),>squarerax=%rax
+movq 104(%rsp),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * rx1_stack
+# asm 1: mulq <rx1_stack=stack64#13
+# asm 2: mulq <rx1_stack=96(%rsp)
+mulq 96(%rsp)
+
+# qhasm: rx3 = squarerax
+# asm 1: mov <squarerax=int64#7,>rx3=int64#6
+# asm 2: mov <squarerax=%rax,>rx3=%r9
+mov %rax,%r9
+
+# qhasm: squarer4 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer4=int64#8
+# asm 2: mov <squarerdx=%rdx,>squarer4=%r10
+mov %rdx,%r10
+
+# qhasm: squarerax = rx3_stack
+# asm 1: movq <rx3_stack=stack64#15,>squarerax=int64#7
+# asm 2: movq <rx3_stack=112(%rsp),>squarerax=%rax
+movq 112(%rsp),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * rx2_stack
+# asm 1: mulq <rx2_stack=stack64#14
+# asm 2: mulq <rx2_stack=104(%rsp)
+mulq 104(%rsp)
+
+# qhasm: squarer5 = squarerax
+# asm 1: mov <squarerax=int64#7,>squarer5=int64#9
+# asm 2: mov <squarerax=%rax,>squarer5=%r11
+mov %rax,%r11
+
+# qhasm: squarer6 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer6=int64#10
+# asm 2: mov <squarerdx=%rdx,>squarer6=%r12
+mov %rdx,%r12
+
+# qhasm: squarerax = rx2_stack
+# asm 1: movq <rx2_stack=stack64#14,>squarerax=int64#7
+# asm 2: movq <rx2_stack=104(%rsp),>squarerax=%rax
+movq 104(%rsp),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * rx0_stack
+# asm 1: mulq <rx0_stack=stack64#12
+# asm 2: mulq <rx0_stack=88(%rsp)
+mulq 88(%rsp)
+
+# qhasm: carry? rx2 += squarerax
+# asm 1: add <squarerax=int64#7,<rx2=int64#5
+# asm 2: add <squarerax=%rax,<rx2=%r8
+add %rax,%r8
+
+# qhasm: carry? rx3 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<rx3=int64#6
+# asm 2: adc <squarerdx=%rdx,<rx3=%r9
+adc %rdx,%r9
+
+# qhasm: squarer4 += 0 + carry
+# asm 1: adc $0,<squarer4=int64#8
+# asm 2: adc $0,<squarer4=%r10
+adc $0,%r10
+
+# qhasm: squarerax = rx3_stack
+# asm 1: movq <rx3_stack=stack64#15,>squarerax=int64#7
+# asm 2: movq <rx3_stack=112(%rsp),>squarerax=%rax
+movq 112(%rsp),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * rx1_stack
+# asm 1: mulq <rx1_stack=stack64#13
+# asm 2: mulq <rx1_stack=96(%rsp)
+mulq 96(%rsp)
+
+# qhasm: carry? squarer4 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer4=int64#8
+# asm 2: add <squarerax=%rax,<squarer4=%r10
+add %rax,%r10
+
+# qhasm: carry? squarer5 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer5=int64#9
+# asm 2: adc <squarerdx=%rdx,<squarer5=%r11
+adc %rdx,%r11
+
+# qhasm: squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#10
+# asm 2: adc $0,<squarer6=%r12
+adc $0,%r12
+
+# qhasm: squarerax = rx3_stack
+# asm 1: movq <rx3_stack=stack64#15,>squarerax=int64#7
+# asm 2: movq <rx3_stack=112(%rsp),>squarerax=%rax
+movq 112(%rsp),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * rx0_stack
+# asm 1: mulq <rx0_stack=stack64#12
+# asm 2: mulq <rx0_stack=88(%rsp)
+mulq 88(%rsp)
+
+# qhasm: carry? rx3 += squarerax
+# asm 1: add <squarerax=int64#7,<rx3=int64#6
+# asm 2: add <squarerax=%rax,<rx3=%r9
+add %rax,%r9
+
+# qhasm: carry? squarer4 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer4=int64#8
+# asm 2: adc <squarerdx=%rdx,<squarer4=%r10
+adc %rdx,%r10
+
+# qhasm: carry? squarer5 += 0 + carry
+# asm 1: adc $0,<squarer5=int64#9
+# asm 2: adc $0,<squarer5=%r11
+adc $0,%r11
+
+# qhasm: carry? squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#10
+# asm 2: adc $0,<squarer6=%r12
+adc $0,%r12
+
+# qhasm: squarer7 += 0 + carry
+# asm 1: adc $0,<squarer7=int64#2
+# asm 2: adc $0,<squarer7=%rsi
+adc $0,%rsi
+
+# qhasm: carry? rx1 += rx1
+# asm 1: add <rx1=int64#4,<rx1=int64#4
+# asm 2: add <rx1=%rcx,<rx1=%rcx
+add %rcx,%rcx
+
+# qhasm: carry? rx2 += rx2 + carry
+# asm 1: adc <rx2=int64#5,<rx2=int64#5
+# asm 2: adc <rx2=%r8,<rx2=%r8
+adc %r8,%r8
+
+# qhasm: carry? rx3 += rx3 + carry
+# asm 1: adc <rx3=int64#6,<rx3=int64#6
+# asm 2: adc <rx3=%r9,<rx3=%r9
+adc %r9,%r9
+
+# qhasm: carry? squarer4 += squarer4 + carry
+# asm 1: adc <squarer4=int64#8,<squarer4=int64#8
+# asm 2: adc <squarer4=%r10,<squarer4=%r10
+adc %r10,%r10
+
+# qhasm: carry? squarer5 += squarer5 + carry
+# asm 1: adc <squarer5=int64#9,<squarer5=int64#9
+# asm 2: adc <squarer5=%r11,<squarer5=%r11
+adc %r11,%r11
+
+# qhasm: carry? squarer6 += squarer6 + carry
+# asm 1: adc <squarer6=int64#10,<squarer6=int64#10
+# asm 2: adc <squarer6=%r12,<squarer6=%r12
+adc %r12,%r12
+
+# qhasm: squarer7 += squarer7 + carry
+# asm 1: adc <squarer7=int64#2,<squarer7=int64#2
+# asm 2: adc <squarer7=%rsi,<squarer7=%rsi
+adc %rsi,%rsi
+
+# qhasm: squarerax = rx0_stack
+# asm 1: movq <rx0_stack=stack64#12,>squarerax=int64#7
+# asm 2: movq <rx0_stack=88(%rsp),>squarerax=%rax
+movq 88(%rsp),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * rx0_stack
+# asm 1: mulq <rx0_stack=stack64#12
+# asm 2: mulq <rx0_stack=88(%rsp)
+mulq 88(%rsp)
+
+# qhasm: rx0 = squarerax
+# asm 1: mov <squarerax=int64#7,>rx0=int64#11
+# asm 2: mov <squarerax=%rax,>rx0=%r13
+mov %rax,%r13
+
+# qhasm: squaret1 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squaret1=int64#12
+# asm 2: mov <squarerdx=%rdx,>squaret1=%r14
+mov %rdx,%r14
+
+# qhasm: squarerax = rx1_stack
+# asm 1: movq <rx1_stack=stack64#13,>squarerax=int64#7
+# asm 2: movq <rx1_stack=96(%rsp),>squarerax=%rax
+movq 96(%rsp),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * rx1_stack
+# asm 1: mulq <rx1_stack=stack64#13
+# asm 2: mulq <rx1_stack=96(%rsp)
+mulq 96(%rsp)
+
+# qhasm: squaret2 = squarerax
+# asm 1: mov <squarerax=int64#7,>squaret2=int64#13
+# asm 2: mov <squarerax=%rax,>squaret2=%r15
+mov %rax,%r15
+
+# qhasm: squaret3 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squaret3=int64#14
+# asm 2: mov <squarerdx=%rdx,>squaret3=%rbx
+mov %rdx,%rbx
+
+# qhasm: squarerax = rx2_stack
+# asm 1: movq <rx2_stack=stack64#14,>squarerax=int64#7
+# asm 2: movq <rx2_stack=104(%rsp),>squarerax=%rax
+movq 104(%rsp),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * rx2_stack
+# asm 1: mulq <rx2_stack=stack64#14
+# asm 2: mulq <rx2_stack=104(%rsp)
+mulq 104(%rsp)
+
+# qhasm: carry? rx1 += squaret1
+# asm 1: add <squaret1=int64#12,<rx1=int64#4
+# asm 2: add <squaret1=%r14,<rx1=%rcx
+add %r14,%rcx
+
+# qhasm: carry? rx2 += squaret2 + carry
+# asm 1: adc <squaret2=int64#13,<rx2=int64#5
+# asm 2: adc <squaret2=%r15,<rx2=%r8
+adc %r15,%r8
+
+# qhasm: carry? rx3 += squaret3 + carry
+# asm 1: adc <squaret3=int64#14,<rx3=int64#6
+# asm 2: adc <squaret3=%rbx,<rx3=%r9
+adc %rbx,%r9
+
+# qhasm: carry? squarer4 += squarerax + carry
+# asm 1: adc <squarerax=int64#7,<squarer4=int64#8
+# asm 2: adc <squarerax=%rax,<squarer4=%r10
+adc %rax,%r10
+
+# qhasm: carry? squarer5 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer5=int64#9
+# asm 2: adc <squarerdx=%rdx,<squarer5=%r11
+adc %rdx,%r11
+
+# qhasm: carry? squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#10
+# asm 2: adc $0,<squarer6=%r12
+adc $0,%r12
+
+# qhasm: squarer7 += 0 + carry
+# asm 1: adc $0,<squarer7=int64#2
+# asm 2: adc $0,<squarer7=%rsi
+adc $0,%rsi
+
+# qhasm: squarerax = rx3_stack
+# asm 1: movq <rx3_stack=stack64#15,>squarerax=int64#7
+# asm 2: movq <rx3_stack=112(%rsp),>squarerax=%rax
+movq 112(%rsp),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * rx3_stack
+# asm 1: mulq <rx3_stack=stack64#15
+# asm 2: mulq <rx3_stack=112(%rsp)
+mulq 112(%rsp)
+
+# qhasm: carry? squarer6 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer6=int64#10
+# asm 2: add <squarerax=%rax,<squarer6=%r12
+add %rax,%r12
+
+# qhasm: squarer7 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer7=int64#2
+# asm 2: adc <squarerdx=%rdx,<squarer7=%rsi
+adc %rdx,%rsi
+
+# qhasm: squarerax = squarer4
+# asm 1: mov <squarer4=int64#8,>squarerax=int64#7
+# asm 2: mov <squarer4=%r10,>squarerax=%rax
+mov %r10,%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: squarer4 = squarerax
+# asm 1: mov <squarerax=int64#7,>squarer4=int64#8
+# asm 2: mov <squarerax=%rax,>squarer4=%r10
+mov %rax,%r10
+
+# qhasm: squarerax = squarer5
+# asm 1: mov <squarer5=int64#9,>squarerax=int64#7
+# asm 2: mov <squarer5=%r11,>squarerax=%rax
+mov %r11,%rax
+
+# qhasm: squarer5 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer5=int64#9
+# asm 2: mov <squarerdx=%rdx,>squarer5=%r11
+mov %rdx,%r11
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? squarer5 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer5=int64#9
+# asm 2: add <squarerax=%rax,<squarer5=%r11
+add %rax,%r11
+
+# qhasm: squarerax = squarer6
+# asm 1: mov <squarer6=int64#10,>squarerax=int64#7
+# asm 2: mov <squarer6=%r12,>squarerax=%rax
+mov %r12,%rax
+
+# qhasm: squarer6 = 0
+# asm 1: mov $0,>squarer6=int64#10
+# asm 2: mov $0,>squarer6=%r12
+mov $0,%r12
+
+# qhasm: squarer6 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer6=int64#10
+# asm 2: adc <squarerdx=%rdx,<squarer6=%r12
+adc %rdx,%r12
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? squarer6 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer6=int64#10
+# asm 2: add <squarerax=%rax,<squarer6=%r12
+add %rax,%r12
+
+# qhasm: squarerax = squarer7
+# asm 1: mov <squarer7=int64#2,>squarerax=int64#7
+# asm 2: mov <squarer7=%rsi,>squarerax=%rax
+mov %rsi,%rax
+
+# qhasm: squarer7 = 0
+# asm 1: mov $0,>squarer7=int64#2
+# asm 2: mov $0,>squarer7=%rsi
+mov $0,%rsi
+
+# qhasm: squarer7 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer7=int64#2
+# asm 2: adc <squarerdx=%rdx,<squarer7=%rsi
+adc %rdx,%rsi
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? squarer7 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer7=int64#2
+# asm 2: add <squarerax=%rax,<squarer7=%rsi
+add %rax,%rsi
+
+# qhasm: squarer8 = 0
+# asm 1: mov $0,>squarer8=int64#7
+# asm 2: mov $0,>squarer8=%rax
+mov $0,%rax
+
+# qhasm: squarer8 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer8=int64#7
+# asm 2: adc <squarerdx=%rdx,<squarer8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rx0 += squarer4
+# asm 1: add <squarer4=int64#8,<rx0=int64#11
+# asm 2: add <squarer4=%r10,<rx0=%r13
+add %r10,%r13
+
+# qhasm: carry? rx1 += squarer5 + carry
+# asm 1: adc <squarer5=int64#9,<rx1=int64#4
+# asm 2: adc <squarer5=%r11,<rx1=%rcx
+adc %r11,%rcx
+
+# qhasm: carry? rx2 += squarer6 + carry
+# asm 1: adc <squarer6=int64#10,<rx2=int64#5
+# asm 2: adc <squarer6=%r12,<rx2=%r8
+adc %r12,%r8
+
+# qhasm: carry? rx3 += squarer7 + carry
+# asm 1: adc <squarer7=int64#2,<rx3=int64#6
+# asm 2: adc <squarer7=%rsi,<rx3=%r9
+adc %rsi,%r9
+
+# qhasm: squarezero = 0
+# asm 1: mov $0,>squarezero=int64#2
+# asm 2: mov $0,>squarezero=%rsi
+mov $0,%rsi
+
+# qhasm: squarer8 += squarezero + carry
+# asm 1: adc <squarezero=int64#2,<squarer8=int64#7
+# asm 2: adc <squarezero=%rsi,<squarer8=%rax
+adc %rsi,%rax
+
+# qhasm: squarer8 *= 38
+# asm 1: imulq $38,<squarer8=int64#7,>squarer8=int64#3
+# asm 2: imulq $38,<squarer8=%rax,>squarer8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? rx0 += squarer8
+# asm 1: add <squarer8=int64#3,<rx0=int64#11
+# asm 2: add <squarer8=%rdx,<rx0=%r13
+add %rdx,%r13
+
+# qhasm: carry? rx1 += squarezero + carry
+# asm 1: adc <squarezero=int64#2,<rx1=int64#4
+# asm 2: adc <squarezero=%rsi,<rx1=%rcx
+adc %rsi,%rcx
+
+# qhasm: carry? rx2 += squarezero + carry
+# asm 1: adc <squarezero=int64#2,<rx2=int64#5
+# asm 2: adc <squarezero=%rsi,<rx2=%r8
+adc %rsi,%r8
+
+# qhasm: carry? rx3 += squarezero + carry
+# asm 1: adc <squarezero=int64#2,<rx3=int64#6
+# asm 2: adc <squarezero=%rsi,<rx3=%r9
+adc %rsi,%r9
+
+# qhasm: squarezero += squarezero + carry
+# asm 1: adc <squarezero=int64#2,<squarezero=int64#2
+# asm 2: adc <squarezero=%rsi,<squarezero=%rsi
+adc %rsi,%rsi
+
+# qhasm: squarezero *= 38
+# asm 1: imulq $38,<squarezero=int64#2,>squarezero=int64#2
+# asm 2: imulq $38,<squarezero=%rsi,>squarezero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: rx0 += squarezero
+# asm 1: add <squarezero=int64#2,<rx0=int64#11
+# asm 2: add <squarezero=%rsi,<rx0=%r13
+add %rsi,%r13
+
+# qhasm: carry? rx0 += d0_stack
+# asm 1: addq <d0_stack=stack64#8,<rx0=int64#11
+# asm 2: addq <d0_stack=56(%rsp),<rx0=%r13
+addq 56(%rsp),%r13
+
+# qhasm: carry? rx1 += d1_stack + carry
+# asm 1: adcq <d1_stack=stack64#9,<rx1=int64#4
+# asm 2: adcq <d1_stack=64(%rsp),<rx1=%rcx
+adcq 64(%rsp),%rcx
+
+# qhasm: carry? rx2 += d2_stack + carry
+# asm 1: adcq <d2_stack=stack64#10,<rx2=int64#5
+# asm 2: adcq <d2_stack=72(%rsp),<rx2=%r8
+adcq 72(%rsp),%r8
+
+# qhasm: carry? rx3 += d3_stack + carry
+# asm 1: adcq <d3_stack=stack64#11,<rx3=int64#6
+# asm 2: adcq <d3_stack=80(%rsp),<rx3=%r9
+adcq 80(%rsp),%r9
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#2
+# asm 2: mov $0,>addt0=%rsi
+mov $0,%rsi
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#3
+# asm 2: mov $38,>addt1=%rdx
+mov $38,%rdx
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#2,<addt1=int64#3
+# asm 2: cmovae <addt0=%rsi,<addt1=%rdx
+cmovae %rsi,%rdx
+
+# qhasm: carry? rx0 += addt1
+# asm 1: add <addt1=int64#3,<rx0=int64#11
+# asm 2: add <addt1=%rdx,<rx0=%r13
+add %rdx,%r13
+
+# qhasm: carry? rx1 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rx1=int64#4
+# asm 2: adc <addt0=%rsi,<rx1=%rcx
+adc %rsi,%rcx
+
+# qhasm: carry? rx2 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rx2=int64#5
+# asm 2: adc <addt0=%rsi,<rx2=%r8
+adc %rsi,%r8
+
+# qhasm: carry? rx3 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rx3=int64#6
+# asm 2: adc <addt0=%rsi,<rx3=%r9
+adc %rsi,%r9
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#3,<addt0=int64#2
+# asm 2: cmovc <addt1=%rdx,<addt0=%rsi
+cmovc %rdx,%rsi
+
+# qhasm: rx0 += addt0
+# asm 1: add <addt0=int64#2,<rx0=int64#11
+# asm 2: add <addt0=%rsi,<rx0=%r13
+add %rsi,%r13
+
+# qhasm: carry? rx0 += e0_stack
+# asm 1: addq <e0_stack=stack64#20,<rx0=int64#11
+# asm 2: addq <e0_stack=152(%rsp),<rx0=%r13
+addq 152(%rsp),%r13
+
+# qhasm: carry? rx1 += e1_stack + carry
+# asm 1: adcq <e1_stack=stack64#21,<rx1=int64#4
+# asm 2: adcq <e1_stack=160(%rsp),<rx1=%rcx
+adcq 160(%rsp),%rcx
+
+# qhasm: carry? rx2 += e2_stack + carry
+# asm 1: adcq <e2_stack=stack64#22,<rx2=int64#5
+# asm 2: adcq <e2_stack=168(%rsp),<rx2=%r8
+adcq 168(%rsp),%r8
+
+# qhasm: carry? rx3 += e3_stack + carry
+# asm 1: adcq <e3_stack=stack64#23,<rx3=int64#6
+# asm 2: adcq <e3_stack=176(%rsp),<rx3=%r9
+adcq 176(%rsp),%r9
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#2
+# asm 2: mov $0,>addt0=%rsi
+mov $0,%rsi
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#3
+# asm 2: mov $38,>addt1=%rdx
+mov $38,%rdx
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#2,<addt1=int64#3
+# asm 2: cmovae <addt0=%rsi,<addt1=%rdx
+cmovae %rsi,%rdx
+
+# qhasm: carry? rx0 += addt1
+# asm 1: add <addt1=int64#3,<rx0=int64#11
+# asm 2: add <addt1=%rdx,<rx0=%r13
+add %rdx,%r13
+
+# qhasm: carry? rx1 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rx1=int64#4
+# asm 2: adc <addt0=%rsi,<rx1=%rcx
+adc %rsi,%rcx
+
+# qhasm: carry? rx2 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rx2=int64#5
+# asm 2: adc <addt0=%rsi,<rx2=%r8
+adc %rsi,%r8
+
+# qhasm: carry? rx3 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rx3=int64#6
+# asm 2: adc <addt0=%rsi,<rx3=%r9
+adc %rsi,%r9
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#3,<addt0=int64#2
+# asm 2: cmovc <addt1=%rdx,<addt0=%rsi
+cmovc %rdx,%rsi
+
+# qhasm: rx0 += addt0
+# asm 1: add <addt0=int64#2,<rx0=int64#11
+# asm 2: add <addt0=%rsi,<rx0=%r13
+add %rsi,%r13
+
+# qhasm: *(uint64 *)(rp + 0) = rx0
+# asm 1: movq <rx0=int64#11,0(<rp=int64#1)
+# asm 2: movq <rx0=%r13,0(<rp=%rdi)
+movq %r13,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = rx1
+# asm 1: movq <rx1=int64#4,8(<rp=int64#1)
+# asm 2: movq <rx1=%rcx,8(<rp=%rdi)
+movq %rcx,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = rx2
+# asm 1: movq <rx2=int64#5,16(<rp=int64#1)
+# asm 2: movq <rx2=%r8,16(<rp=%rdi)
+movq %r8,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = rx3
+# asm 1: movq <rx3=int64#6,24(<rp=int64#1)
+# asm 2: movq <rx3=%r9,24(<rp=%rdi)
+movq %r9,24(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/ge25519_double.c b/ext/ed25519-amd64-asm/ge25519_double.c
new file mode 100644
index 00000000..d55e2b4f
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_double.c
@@ -0,0 +1,8 @@
+#include "ge25519.h"
+
+void ge25519_double(ge25519_p3 *r, const ge25519_p3 *p)
+{
+ ge25519_p1p1 grp1p1;
+ ge25519_dbl_p1p1(&grp1p1, (ge25519_p2 *)p);
+ ge25519_p1p1_to_p3(r, &grp1p1);
+}
diff --git a/ext/ed25519-amd64-asm/ge25519_double_scalarmult.c b/ext/ed25519-amd64-asm/ge25519_double_scalarmult.c
new file mode 100644
index 00000000..30c922af
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_double_scalarmult.c
@@ -0,0 +1,102 @@
+#include "fe25519.h"
+#include "sc25519.h"
+#include "ge25519.h"
+
+#define S1_SWINDOWSIZE 5
+#define PRE1_SIZE (1<<(S1_SWINDOWSIZE-2))
+#define S2_SWINDOWSIZE 7
+#define PRE2_SIZE (1<<(S2_SWINDOWSIZE-2))
+
+ge25519_niels pre2[PRE2_SIZE] = {
+#include "ge25519_base_slide_multiples.data"
+};
+
+static const fe25519 ec2d = {{0xEBD69B9426B2F146, 0x00E0149A8283B156, 0x198E80F2EEF3D130, 0xA406D9DC56DFFCE7}};
+
+static void setneutral(ge25519 *r)
+{
+ fe25519_setint(&r->x,0);
+ fe25519_setint(&r->y,1);
+ fe25519_setint(&r->z,1);
+ fe25519_setint(&r->t,0);
+}
+
+/* computes [s1]p1 + [s2]p2 */
+void ge25519_double_scalarmult_vartime(ge25519_p3 *r, const ge25519_p3 *p1, const sc25519 *s1, const sc25519 *s2)
+{
+ signed char slide1[256], slide2[256];
+ ge25519_pniels pre1[PRE1_SIZE], neg;
+ ge25519_p3 d1;
+ ge25519_p1p1 t;
+ ge25519_niels nneg;
+ fe25519 d;
+ int i;
+
+ sc25519_slide(slide1, s1, S1_SWINDOWSIZE);
+ sc25519_slide(slide2, s2, S2_SWINDOWSIZE);
+
+ /* precomputation */
+ pre1[0] = *(ge25519_pniels *)p1;
+ ge25519_dbl_p1p1(&t,(ge25519_p2 *)pre1); ge25519_p1p1_to_p3(&d1, &t);
+ /* Convert pre[0] to projective Niels representation */
+ d = pre1[0].ysubx;
+ fe25519_sub(&pre1[0].ysubx, &pre1[0].xaddy, &pre1[0].ysubx);
+ fe25519_add(&pre1[0].xaddy, &pre1[0].xaddy, &d);
+ fe25519_mul(&pre1[0].t2d, &pre1[0].t2d, &ec2d);
+
+ for(i=0;i<PRE1_SIZE-1;i++)
+ {
+ ge25519_pnielsadd_p1p1(&t, &d1, &pre1[i]); ge25519_p1p1_to_p3((ge25519_p3 *)&pre1[i+1], &t);
+ /* Convert pre1[i+1] to projective Niels representation */
+ d = pre1[i+1].ysubx;
+ fe25519_sub(&pre1[i+1].ysubx, &pre1[i+1].xaddy, &pre1[i+1].ysubx);
+ fe25519_add(&pre1[i+1].xaddy, &pre1[i+1].xaddy, &d);
+ fe25519_mul(&pre1[i+1].t2d, &pre1[i+1].t2d, &ec2d);
+ }
+
+ setneutral(r);
+ for (i = 255;i >= 0;--i) {
+ if (slide1[i] || slide2[i]) goto firstbit;
+ }
+
+ for(;i>=0;i--)
+ {
+ firstbit:
+
+ ge25519_dbl_p1p1(&t, (ge25519_p2 *)r);
+
+ if(slide1[i]>0)
+ {
+ ge25519_p1p1_to_p3(r, &t);
+ ge25519_pnielsadd_p1p1(&t, r, &pre1[slide1[i]/2]);
+ }
+ else if(slide1[i]<0)
+ {
+ ge25519_p1p1_to_p3(r, &t);
+ neg = pre1[-slide1[i]/2];
+ d = neg.ysubx;
+ neg.ysubx = neg.xaddy;
+ neg.xaddy = d;
+ fe25519_neg(&neg.t2d, &neg.t2d);
+ ge25519_pnielsadd_p1p1(&t, r, &neg);
+ }
+
+ if(slide2[i]>0)
+ {
+ ge25519_p1p1_to_p3(r, &t);
+ ge25519_nielsadd_p1p1(&t, r, &pre2[slide2[i]/2]);
+ }
+ else if(slide2[i]<0)
+ {
+ ge25519_p1p1_to_p3(r, &t);
+ nneg = pre2[-slide2[i]/2];
+ d = nneg.ysubx;
+ nneg.ysubx = nneg.xaddy;
+ nneg.xaddy = d;
+ fe25519_neg(&nneg.t2d, &nneg.t2d);
+ ge25519_nielsadd_p1p1(&t, r, &nneg);
+ }
+
+ ge25519_p1p1_to_p2((ge25519_p2 *)r, &t);
+ }
+}
diff --git a/ext/ed25519-amd64-asm/ge25519_isneutral.c b/ext/ed25519-amd64-asm/ge25519_isneutral.c
new file mode 100644
index 00000000..cf566dba
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_isneutral.c
@@ -0,0 +1,9 @@
+#include "fe25519.h"
+#include "ge25519.h"
+
+int ge25519_isneutral_vartime(const ge25519_p3 *p)
+{
+ if(!fe25519_iszero_vartime(&p->x)) return 0;
+ if(!fe25519_iseq_vartime(&p->y, &p->z)) return 0;
+ return 1;
+}
diff --git a/ext/ed25519-amd64-asm/ge25519_multi_scalarmult.c b/ext/ed25519-amd64-asm/ge25519_multi_scalarmult.c
new file mode 100644
index 00000000..afc6aeae
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_multi_scalarmult.c
@@ -0,0 +1,102 @@
+#include "fe25519.h"
+#include "sc25519.h"
+#include "ge25519.h"
+#include "index_heap.h"
+
+static void setneutral(ge25519 *r)
+{
+ fe25519_setint(&r->x,0);
+ fe25519_setint(&r->y,1);
+ fe25519_setint(&r->z,1);
+ fe25519_setint(&r->t,0);
+}
+
+static void ge25519_scalarmult_vartime_2limbs(ge25519 *r, ge25519 *p, sc25519 *s)
+{
+ if (s->v[1] == 0 && s->v[0] == 1) /* This will happen most of the time after Bos-Coster */
+ *r = *p;
+ else if (s->v[1] == 0 && s->v[0] == 0) /* This won't ever happen, except for all scalars == 0 in Bos-Coster */
+ setneutral(r);
+ else
+ {
+ ge25519 d;
+ unsigned long long mask = (1ULL << 63);
+ int i = 1;
+ while(!(mask & s->v[1]) && mask != 0)
+ mask >>= 1;
+ if(mask == 0)
+ {
+ mask = (1ULL << 63);
+ i = 0;
+ while(!(mask & s->v[0]) && mask != 0)
+ mask >>= 1;
+ }
+ d = *p;
+ mask >>= 1;
+ for(;mask != 0;mask >>= 1)
+ {
+ ge25519_double(&d,&d);
+ if(s->v[i] & mask)
+ ge25519_add(&d,&d,p);
+ }
+ if(i==1)
+ {
+ mask = (1ULL << 63);
+ for(;mask != 0;mask >>= 1)
+ {
+ ge25519_double(&d,&d);
+ if(s->v[0] & mask)
+ ge25519_add(&d,&d,p);
+ }
+ }
+ *r = d;
+ }
+}
+
+/* caller's responsibility to ensure npoints >= 5 */
+void ge25519_multi_scalarmult_vartime(ge25519_p3 *r, ge25519_p3 *p, sc25519 *s, const unsigned long long npoints)
+{
+ unsigned long long pos[npoints];
+ unsigned long long hlen=((npoints+1)/2)|1;
+ unsigned long long max1, max2,i;
+
+ heap_init(pos, hlen, s);
+
+ for(i=0;;i++)
+ {
+ heap_get2max(pos, &max1, &max2, s);
+ if((s[max1].v[3] == 0) || (sc25519_iszero_vartime(&s[max2]))) break;
+ sc25519_sub_nored(&s[max1],&s[max1],&s[max2]);
+ ge25519_add(&p[max2],&p[max2],&p[max1]);
+ heap_rootreplaced(pos, hlen, s);
+ }
+ for(;;i++)
+ {
+ heap_get2max(pos, &max1, &max2, s);
+ if((s[max1].v[2] == 0) || (sc25519_iszero_vartime(&s[max2]))) break;
+ sc25519_sub_nored(&s[max1],&s[max1],&s[max2]);
+ ge25519_add(&p[max2],&p[max2],&p[max1]);
+ heap_rootreplaced_3limbs(pos, hlen, s);
+ }
+ /* We know that (npoints-1)/2 scalars are only 128-bit scalars */
+ heap_extend(pos, hlen, npoints, s);
+ hlen = npoints;
+ for(;;i++)
+ {
+ heap_get2max(pos, &max1, &max2, s);
+ if((s[max1].v[1] == 0) || (sc25519_iszero_vartime(&s[max2]))) break;
+ sc25519_sub_nored(&s[max1],&s[max1],&s[max2]);
+ ge25519_add(&p[max2],&p[max2],&p[max1]);
+ heap_rootreplaced_2limbs(pos, hlen, s);
+ }
+ for(;;i++)
+ {
+ heap_get2max(pos, &max1, &max2, s);
+ if(sc25519_iszero_vartime(&s[max2])) break;
+ sc25519_sub_nored(&s[max1],&s[max1],&s[max2]);
+ ge25519_add(&p[max2],&p[max2],&p[max1]);
+ heap_rootreplaced_1limb(pos, hlen, s);
+ }
+
+ ge25519_scalarmult_vartime_2limbs(r, &p[max1], &s[max1]);
+}
diff --git a/ext/ed25519-amd64-asm/ge25519_nielsadd2.s b/ext/ed25519-amd64-asm/ge25519_nielsadd2.s
new file mode 100644
index 00000000..ec31e023
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_nielsadd2.s
@@ -0,0 +1,5791 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 qp
+
+# qhasm: input rp
+
+# qhasm: input qp
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: int64 a0
+
+# qhasm: int64 a1
+
+# qhasm: int64 a2
+
+# qhasm: int64 a3
+
+# qhasm: stack64 a0_stack
+
+# qhasm: stack64 a1_stack
+
+# qhasm: stack64 a2_stack
+
+# qhasm: stack64 a3_stack
+
+# qhasm: int64 b0
+
+# qhasm: int64 b1
+
+# qhasm: int64 b2
+
+# qhasm: int64 b3
+
+# qhasm: stack64 b0_stack
+
+# qhasm: stack64 b1_stack
+
+# qhasm: stack64 b2_stack
+
+# qhasm: stack64 b3_stack
+
+# qhasm: int64 c0
+
+# qhasm: int64 c1
+
+# qhasm: int64 c2
+
+# qhasm: int64 c3
+
+# qhasm: stack64 c0_stack
+
+# qhasm: stack64 c1_stack
+
+# qhasm: stack64 c2_stack
+
+# qhasm: stack64 c3_stack
+
+# qhasm: int64 d0
+
+# qhasm: int64 d1
+
+# qhasm: int64 d2
+
+# qhasm: int64 d3
+
+# qhasm: stack64 d0_stack
+
+# qhasm: stack64 d1_stack
+
+# qhasm: stack64 d2_stack
+
+# qhasm: stack64 d3_stack
+
+# qhasm: int64 e0
+
+# qhasm: int64 e1
+
+# qhasm: int64 e2
+
+# qhasm: int64 e3
+
+# qhasm: stack64 e0_stack
+
+# qhasm: stack64 e1_stack
+
+# qhasm: stack64 e2_stack
+
+# qhasm: stack64 e3_stack
+
+# qhasm: int64 f0
+
+# qhasm: int64 f1
+
+# qhasm: int64 f2
+
+# qhasm: int64 f3
+
+# qhasm: stack64 f0_stack
+
+# qhasm: stack64 f1_stack
+
+# qhasm: stack64 f2_stack
+
+# qhasm: stack64 f3_stack
+
+# qhasm: int64 g0
+
+# qhasm: int64 g1
+
+# qhasm: int64 g2
+
+# qhasm: int64 g3
+
+# qhasm: stack64 g0_stack
+
+# qhasm: stack64 g1_stack
+
+# qhasm: stack64 g2_stack
+
+# qhasm: stack64 g3_stack
+
+# qhasm: int64 h0
+
+# qhasm: int64 h1
+
+# qhasm: int64 h2
+
+# qhasm: int64 h3
+
+# qhasm: stack64 h0_stack
+
+# qhasm: stack64 h1_stack
+
+# qhasm: stack64 h2_stack
+
+# qhasm: stack64 h3_stack
+
+# qhasm: int64 qt0
+
+# qhasm: int64 qt1
+
+# qhasm: int64 qt2
+
+# qhasm: int64 qt3
+
+# qhasm: stack64 qt0_stack
+
+# qhasm: stack64 qt1_stack
+
+# qhasm: stack64 qt2_stack
+
+# qhasm: stack64 qt3_stack
+
+# qhasm: int64 t10
+
+# qhasm: int64 t11
+
+# qhasm: int64 t12
+
+# qhasm: int64 t13
+
+# qhasm: stack64 t10_stack
+
+# qhasm: stack64 t11_stack
+
+# qhasm: stack64 t12_stack
+
+# qhasm: stack64 t13_stack
+
+# qhasm: int64 t20
+
+# qhasm: int64 t21
+
+# qhasm: int64 t22
+
+# qhasm: int64 t23
+
+# qhasm: stack64 t20_stack
+
+# qhasm: stack64 t21_stack
+
+# qhasm: stack64 t22_stack
+
+# qhasm: stack64 t23_stack
+
+# qhasm: int64 rx0
+
+# qhasm: int64 rx1
+
+# qhasm: int64 rx2
+
+# qhasm: int64 rx3
+
+# qhasm: int64 ry0
+
+# qhasm: int64 ry1
+
+# qhasm: int64 ry2
+
+# qhasm: int64 ry3
+
+# qhasm: int64 rz0
+
+# qhasm: int64 rz1
+
+# qhasm: int64 rz2
+
+# qhasm: int64 rz3
+
+# qhasm: int64 rt0
+
+# qhasm: int64 rt1
+
+# qhasm: int64 rt2
+
+# qhasm: int64 rt3
+
+# qhasm: int64 mulr4
+
+# qhasm: int64 mulr5
+
+# qhasm: int64 mulr6
+
+# qhasm: int64 mulr7
+
+# qhasm: int64 mulr8
+
+# qhasm: int64 mulrax
+
+# qhasm: int64 mulrdx
+
+# qhasm: int64 mulx0
+
+# qhasm: int64 mulx1
+
+# qhasm: int64 mulx2
+
+# qhasm: int64 mulx3
+
+# qhasm: int64 mulc
+
+# qhasm: int64 mulzero
+
+# qhasm: int64 muli38
+
+# qhasm: int64 addt0
+
+# qhasm: int64 addt1
+
+# qhasm: int64 subt0
+
+# qhasm: int64 subt1
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_ge25519_nielsadd2
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_ge25519_nielsadd2
+.globl crypto_sign_ed25519_amd64_64_ge25519_nielsadd2
+_crypto_sign_ed25519_amd64_64_ge25519_nielsadd2:
+crypto_sign_ed25519_amd64_64_ge25519_nielsadd2:
+mov %rsp,%r11
+and $31,%r11
+add $192,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: a0 = *(uint64 *)(rp + 32)
+# asm 1: movq 32(<rp=int64#1),>a0=int64#3
+# asm 2: movq 32(<rp=%rdi),>a0=%rdx
+movq 32(%rdi),%rdx
+
+# qhasm: a1 = *(uint64 *)(rp + 40)
+# asm 1: movq 40(<rp=int64#1),>a1=int64#4
+# asm 2: movq 40(<rp=%rdi),>a1=%rcx
+movq 40(%rdi),%rcx
+
+# qhasm: a2 = *(uint64 *)(rp + 48)
+# asm 1: movq 48(<rp=int64#1),>a2=int64#5
+# asm 2: movq 48(<rp=%rdi),>a2=%r8
+movq 48(%rdi),%r8
+
+# qhasm: a3 = *(uint64 *)(rp + 56)
+# asm 1: movq 56(<rp=int64#1),>a3=int64#6
+# asm 2: movq 56(<rp=%rdi),>a3=%r9
+movq 56(%rdi),%r9
+
+# qhasm: b0 = a0
+# asm 1: mov <a0=int64#3,>b0=int64#7
+# asm 2: mov <a0=%rdx,>b0=%rax
+mov %rdx,%rax
+
+# qhasm: b1 = a1
+# asm 1: mov <a1=int64#4,>b1=int64#8
+# asm 2: mov <a1=%rcx,>b1=%r10
+mov %rcx,%r10
+
+# qhasm: b2 = a2
+# asm 1: mov <a2=int64#5,>b2=int64#9
+# asm 2: mov <a2=%r8,>b2=%r11
+mov %r8,%r11
+
+# qhasm: b3 = a3
+# asm 1: mov <a3=int64#6,>b3=int64#10
+# asm 2: mov <a3=%r9,>b3=%r12
+mov %r9,%r12
+
+# qhasm: carry? a0 -= *(uint64 *) (rp + 0)
+# asm 1: subq 0(<rp=int64#1),<a0=int64#3
+# asm 2: subq 0(<rp=%rdi),<a0=%rdx
+subq 0(%rdi),%rdx
+
+# qhasm: carry? a1 -= *(uint64 *) (rp + 8) - carry
+# asm 1: sbbq 8(<rp=int64#1),<a1=int64#4
+# asm 2: sbbq 8(<rp=%rdi),<a1=%rcx
+sbbq 8(%rdi),%rcx
+
+# qhasm: carry? a2 -= *(uint64 *) (rp + 16) - carry
+# asm 1: sbbq 16(<rp=int64#1),<a2=int64#5
+# asm 2: sbbq 16(<rp=%rdi),<a2=%r8
+sbbq 16(%rdi),%r8
+
+# qhasm: carry? a3 -= *(uint64 *) (rp + 24) - carry
+# asm 1: sbbq 24(<rp=int64#1),<a3=int64#6
+# asm 2: sbbq 24(<rp=%rdi),<a3=%r9
+sbbq 24(%rdi),%r9
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#11
+# asm 2: mov $0,>subt0=%r13
+mov $0,%r13
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#12
+# asm 2: mov $38,>subt1=%r14
+mov $38,%r14
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#11,<subt1=int64#12
+# asm 2: cmovae <subt0=%r13,<subt1=%r14
+cmovae %r13,%r14
+
+# qhasm: carry? a0 -= subt1
+# asm 1: sub <subt1=int64#12,<a0=int64#3
+# asm 2: sub <subt1=%r14,<a0=%rdx
+sub %r14,%rdx
+
+# qhasm: carry? a1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#11,<a1=int64#4
+# asm 2: sbb <subt0=%r13,<a1=%rcx
+sbb %r13,%rcx
+
+# qhasm: carry? a2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#11,<a2=int64#5
+# asm 2: sbb <subt0=%r13,<a2=%r8
+sbb %r13,%r8
+
+# qhasm: carry? a3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#11,<a3=int64#6
+# asm 2: sbb <subt0=%r13,<a3=%r9
+sbb %r13,%r9
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#12,<subt0=int64#11
+# asm 2: cmovc <subt1=%r14,<subt0=%r13
+cmovc %r14,%r13
+
+# qhasm: a0 -= subt0
+# asm 1: sub <subt0=int64#11,<a0=int64#3
+# asm 2: sub <subt0=%r13,<a0=%rdx
+sub %r13,%rdx
+
+# qhasm: carry? b0 += *(uint64 *) (rp + 0)
+# asm 1: addq 0(<rp=int64#1),<b0=int64#7
+# asm 2: addq 0(<rp=%rdi),<b0=%rax
+addq 0(%rdi),%rax
+
+# qhasm: carry? b1 += *(uint64 *) (rp + 8) + carry
+# asm 1: adcq 8(<rp=int64#1),<b1=int64#8
+# asm 2: adcq 8(<rp=%rdi),<b1=%r10
+adcq 8(%rdi),%r10
+
+# qhasm: carry? b2 += *(uint64 *) (rp + 16) + carry
+# asm 1: adcq 16(<rp=int64#1),<b2=int64#9
+# asm 2: adcq 16(<rp=%rdi),<b2=%r11
+adcq 16(%rdi),%r11
+
+# qhasm: carry? b3 += *(uint64 *) (rp + 24) + carry
+# asm 1: adcq 24(<rp=int64#1),<b3=int64#10
+# asm 2: adcq 24(<rp=%rdi),<b3=%r12
+adcq 24(%rdi),%r12
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#11
+# asm 2: mov $0,>addt0=%r13
+mov $0,%r13
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#12
+# asm 2: mov $38,>addt1=%r14
+mov $38,%r14
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#11,<addt1=int64#12
+# asm 2: cmovae <addt0=%r13,<addt1=%r14
+cmovae %r13,%r14
+
+# qhasm: carry? b0 += addt1
+# asm 1: add <addt1=int64#12,<b0=int64#7
+# asm 2: add <addt1=%r14,<b0=%rax
+add %r14,%rax
+
+# qhasm: carry? b1 += addt0 + carry
+# asm 1: adc <addt0=int64#11,<b1=int64#8
+# asm 2: adc <addt0=%r13,<b1=%r10
+adc %r13,%r10
+
+# qhasm: carry? b2 += addt0 + carry
+# asm 1: adc <addt0=int64#11,<b2=int64#9
+# asm 2: adc <addt0=%r13,<b2=%r11
+adc %r13,%r11
+
+# qhasm: carry? b3 += addt0 + carry
+# asm 1: adc <addt0=int64#11,<b3=int64#10
+# asm 2: adc <addt0=%r13,<b3=%r12
+adc %r13,%r12
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#12,<addt0=int64#11
+# asm 2: cmovc <addt1=%r14,<addt0=%r13
+cmovc %r14,%r13
+
+# qhasm: b0 += addt0
+# asm 1: add <addt0=int64#11,<b0=int64#7
+# asm 2: add <addt0=%r13,<b0=%rax
+add %r13,%rax
+
+# qhasm: a0_stack = a0
+# asm 1: movq <a0=int64#3,>a0_stack=stack64#8
+# asm 2: movq <a0=%rdx,>a0_stack=56(%rsp)
+movq %rdx,56(%rsp)
+
+# qhasm: a1_stack = a1
+# asm 1: movq <a1=int64#4,>a1_stack=stack64#9
+# asm 2: movq <a1=%rcx,>a1_stack=64(%rsp)
+movq %rcx,64(%rsp)
+
+# qhasm: a2_stack = a2
+# asm 1: movq <a2=int64#5,>a2_stack=stack64#10
+# asm 2: movq <a2=%r8,>a2_stack=72(%rsp)
+movq %r8,72(%rsp)
+
+# qhasm: a3_stack = a3
+# asm 1: movq <a3=int64#6,>a3_stack=stack64#11
+# asm 2: movq <a3=%r9,>a3_stack=80(%rsp)
+movq %r9,80(%rsp)
+
+# qhasm: b0_stack = b0
+# asm 1: movq <b0=int64#7,>b0_stack=stack64#12
+# asm 2: movq <b0=%rax,>b0_stack=88(%rsp)
+movq %rax,88(%rsp)
+
+# qhasm: b1_stack = b1
+# asm 1: movq <b1=int64#8,>b1_stack=stack64#13
+# asm 2: movq <b1=%r10,>b1_stack=96(%rsp)
+movq %r10,96(%rsp)
+
+# qhasm: b2_stack = b2
+# asm 1: movq <b2=int64#9,>b2_stack=stack64#14
+# asm 2: movq <b2=%r11,>b2_stack=104(%rsp)
+movq %r11,104(%rsp)
+
+# qhasm: b3_stack = b3
+# asm 1: movq <b3=int64#10,>b3_stack=stack64#15
+# asm 2: movq <b3=%r12,>b3_stack=112(%rsp)
+movq %r12,112(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#4
+# asm 2: mov $0,>mulr4=%rcx
+mov $0,%rcx
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#5
+# asm 2: mov $0,>mulr5=%r8
+mov $0,%r8
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulx0 = a0_stack
+# asm 1: movq <a0_stack=stack64#8,>mulx0=int64#9
+# asm 2: movq <a0_stack=56(%rsp),>mulx0=%r11
+movq 56(%rsp),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rsi),>mulrax=%rax
+movq 0(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: a0 = mulrax
+# asm 1: mov <mulrax=int64#7,>a0=int64#10
+# asm 2: mov <mulrax=%rax,>a0=%r12
+mov %rax,%r12
+
+# qhasm: a1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>a1=int64#11
+# asm 2: mov <mulrdx=%rdx,>a1=%r13
+mov %rdx,%r13
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rsi),>mulrax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? a1 += mulrax
+# asm 1: add <mulrax=int64#7,<a1=int64#11
+# asm 2: add <mulrax=%rax,<a1=%r13
+add %rax,%r13
+
+# qhasm: a2 = 0
+# asm 1: mov $0,>a2=int64#12
+# asm 2: mov $0,>a2=%r14
+mov $0,%r14
+
+# qhasm: a2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<a2=int64#12
+# asm 2: adc <mulrdx=%rdx,<a2=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rsi),>mulrax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#12
+# asm 2: add <mulrax=%rax,<a2=%r14
+add %rax,%r14
+
+# qhasm: a3 = 0
+# asm 1: mov $0,>a3=int64#13
+# asm 2: mov $0,>a3=%r15
+mov $0,%r15
+
+# qhasm: a3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<a3=int64#13
+# asm 2: adc <mulrdx=%rdx,<a3=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rsi),>mulrax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#13
+# asm 2: add <mulrax=%rax,<a3=%r15
+add %rax,%r15
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx1 = a1_stack
+# asm 1: movq <a1_stack=stack64#9,>mulx1=int64#9
+# asm 2: movq <a1_stack=64(%rsp),>mulx1=%r11
+movq 64(%rsp),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rsi),>mulrax=%rax
+movq 0(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? a1 += mulrax
+# asm 1: add <mulrax=int64#7,<a1=int64#11
+# asm 2: add <mulrax=%rax,<a1=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rsi),>mulrax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#12
+# asm 2: add <mulrax=%rax,<a2=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a2 += mulc
+# asm 1: add <mulc=int64#14,<a2=int64#12
+# asm 2: add <mulc=%rbx,<a2=%r14
+add %rbx,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rsi),>mulrax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#13
+# asm 2: add <mulrax=%rax,<a3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a3 += mulc
+# asm 1: add <mulc=int64#14,<a3=int64#13
+# asm 2: add <mulc=%rbx,<a3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rsi),>mulrax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r8
+adc %rdx,%r8
+
+# qhasm: mulx2 = a2_stack
+# asm 1: movq <a2_stack=stack64#10,>mulx2=int64#9
+# asm 2: movq <a2_stack=72(%rsp),>mulx2=%r11
+movq 72(%rsp),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rsi),>mulrax=%rax
+movq 0(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#12
+# asm 2: add <mulrax=%rax,<a2=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rsi),>mulrax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#13
+# asm 2: add <mulrax=%rax,<a3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a3 += mulc
+# asm 1: add <mulc=int64#14,<a3=int64#13
+# asm 2: add <mulc=%rbx,<a3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rsi),>mulrax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rsi),>mulrax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: mulx3 = a3_stack
+# asm 1: movq <a3_stack=stack64#11,>mulx3=int64#9
+# asm 2: movq <a3_stack=80(%rsp),>mulx3=%r11
+movq 80(%rsp),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rsi),>mulrax=%rax
+movq 0(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#13
+# asm 2: add <mulrax=%rax,<a3=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rsi),>mulrax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rsi),>mulrax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rsi),>mulrax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#14,<mulr6=int64#6
+# asm 2: add <mulc=%rbx,<mulr6=%r9
+add %rbx,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr4=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#4
+# asm 2: mov <mulrax=%rax,>mulr4=%rcx
+mov %rax,%rcx
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr5=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#5
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r8
+mov %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr6=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr7=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#8
+# asm 2: add <mulrax=%rax,<mulr7=%r10
+add %rax,%r10
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? a0 += mulr4
+# asm 1: add <mulr4=int64#4,<a0=int64#10
+# asm 2: add <mulr4=%rcx,<a0=%r12
+add %rcx,%r12
+
+# qhasm: carry? a1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#5,<a1=int64#11
+# asm 2: adc <mulr5=%r8,<a1=%r13
+adc %r8,%r13
+
+# qhasm: carry? a2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#6,<a2=int64#12
+# asm 2: adc <mulr6=%r9,<a2=%r14
+adc %r9,%r14
+
+# qhasm: carry? a3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#8,<a3=int64#13
+# asm 2: adc <mulr7=%r10,<a3=%r15
+adc %r10,%r15
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#4
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rcx
+imulq $38,%rax,%rcx
+
+# qhasm: carry? a0 += mulr8
+# asm 1: add <mulr8=int64#4,<a0=int64#10
+# asm 2: add <mulr8=%rcx,<a0=%r12
+add %rcx,%r12
+
+# qhasm: carry? a1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a1=int64#11
+# asm 2: adc <mulzero=%rdx,<a1=%r13
+adc %rdx,%r13
+
+# qhasm: carry? a2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a2=int64#12
+# asm 2: adc <mulzero=%rdx,<a2=%r14
+adc %rdx,%r14
+
+# qhasm: carry? a3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a3=int64#13
+# asm 2: adc <mulzero=%rdx,<a3=%r15
+adc %rdx,%r15
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: a0 += mulzero
+# asm 1: add <mulzero=int64#3,<a0=int64#10
+# asm 2: add <mulzero=%rdx,<a0=%r12
+add %rdx,%r12
+
+# qhasm: a0_stack = a0
+# asm 1: movq <a0=int64#10,>a0_stack=stack64#8
+# asm 2: movq <a0=%r12,>a0_stack=56(%rsp)
+movq %r12,56(%rsp)
+
+# qhasm: a1_stack = a1
+# asm 1: movq <a1=int64#11,>a1_stack=stack64#9
+# asm 2: movq <a1=%r13,>a1_stack=64(%rsp)
+movq %r13,64(%rsp)
+
+# qhasm: a2_stack = a2
+# asm 1: movq <a2=int64#12,>a2_stack=stack64#10
+# asm 2: movq <a2=%r14,>a2_stack=72(%rsp)
+movq %r14,72(%rsp)
+
+# qhasm: a3_stack = a3
+# asm 1: movq <a3=int64#13,>a3_stack=stack64#11
+# asm 2: movq <a3=%r15,>a3_stack=80(%rsp)
+movq %r15,80(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#4
+# asm 2: mov $0,>mulr4=%rcx
+mov $0,%rcx
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#5
+# asm 2: mov $0,>mulr5=%r8
+mov $0,%r8
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulx0 = b0_stack
+# asm 1: movq <b0_stack=stack64#12,>mulx0=int64#9
+# asm 2: movq <b0_stack=88(%rsp),>mulx0=%r11
+movq 88(%rsp),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: e0 = mulrax
+# asm 1: mov <mulrax=int64#7,>e0=int64#10
+# asm 2: mov <mulrax=%rax,>e0=%r12
+mov %rax,%r12
+
+# qhasm: e1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>e1=int64#11
+# asm 2: mov <mulrdx=%rdx,>e1=%r13
+mov %rdx,%r13
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? e1 += mulrax
+# asm 1: add <mulrax=int64#7,<e1=int64#11
+# asm 2: add <mulrax=%rax,<e1=%r13
+add %rax,%r13
+
+# qhasm: e2 = 0
+# asm 1: mov $0,>e2=int64#12
+# asm 2: mov $0,>e2=%r14
+mov $0,%r14
+
+# qhasm: e2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<e2=int64#12
+# asm 2: adc <mulrdx=%rdx,<e2=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? e2 += mulrax
+# asm 1: add <mulrax=int64#7,<e2=int64#12
+# asm 2: add <mulrax=%rax,<e2=%r14
+add %rax,%r14
+
+# qhasm: e3 = 0
+# asm 1: mov $0,>e3=int64#13
+# asm 2: mov $0,>e3=%r15
+mov $0,%r15
+
+# qhasm: e3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<e3=int64#13
+# asm 2: adc <mulrdx=%rdx,<e3=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? e3 += mulrax
+# asm 1: add <mulrax=int64#7,<e3=int64#13
+# asm 2: add <mulrax=%rax,<e3=%r15
+add %rax,%r15
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx1 = b1_stack
+# asm 1: movq <b1_stack=stack64#13,>mulx1=int64#9
+# asm 2: movq <b1_stack=96(%rsp),>mulx1=%r11
+movq 96(%rsp),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? e1 += mulrax
+# asm 1: add <mulrax=int64#7,<e1=int64#11
+# asm 2: add <mulrax=%rax,<e1=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? e2 += mulrax
+# asm 1: add <mulrax=int64#7,<e2=int64#12
+# asm 2: add <mulrax=%rax,<e2=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? e2 += mulc
+# asm 1: add <mulc=int64#14,<e2=int64#12
+# asm 2: add <mulc=%rbx,<e2=%r14
+add %rbx,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? e3 += mulrax
+# asm 1: add <mulrax=int64#7,<e3=int64#13
+# asm 2: add <mulrax=%rax,<e3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? e3 += mulc
+# asm 1: add <mulc=int64#14,<e3=int64#13
+# asm 2: add <mulc=%rbx,<e3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r8
+adc %rdx,%r8
+
+# qhasm: mulx2 = b2_stack
+# asm 1: movq <b2_stack=stack64#14,>mulx2=int64#9
+# asm 2: movq <b2_stack=104(%rsp),>mulx2=%r11
+movq 104(%rsp),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? e2 += mulrax
+# asm 1: add <mulrax=int64#7,<e2=int64#12
+# asm 2: add <mulrax=%rax,<e2=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? e3 += mulrax
+# asm 1: add <mulrax=int64#7,<e3=int64#13
+# asm 2: add <mulrax=%rax,<e3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? e3 += mulc
+# asm 1: add <mulc=int64#14,<e3=int64#13
+# asm 2: add <mulc=%rbx,<e3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: mulx3 = b3_stack
+# asm 1: movq <b3_stack=stack64#15,>mulx3=int64#9
+# asm 2: movq <b3_stack=112(%rsp),>mulx3=%r11
+movq 112(%rsp),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? e3 += mulrax
+# asm 1: add <mulrax=int64#7,<e3=int64#13
+# asm 2: add <mulrax=%rax,<e3=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#14,<mulr6=int64#6
+# asm 2: add <mulc=%rbx,<mulr6=%r9
+add %rbx,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr4=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#4
+# asm 2: mov <mulrax=%rax,>mulr4=%rcx
+mov %rax,%rcx
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr5=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#5
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r8
+mov %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr6=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr7=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#8
+# asm 2: add <mulrax=%rax,<mulr7=%r10
+add %rax,%r10
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? e0 += mulr4
+# asm 1: add <mulr4=int64#4,<e0=int64#10
+# asm 2: add <mulr4=%rcx,<e0=%r12
+add %rcx,%r12
+
+# qhasm: carry? e1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#5,<e1=int64#11
+# asm 2: adc <mulr5=%r8,<e1=%r13
+adc %r8,%r13
+
+# qhasm: carry? e2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#6,<e2=int64#12
+# asm 2: adc <mulr6=%r9,<e2=%r14
+adc %r9,%r14
+
+# qhasm: carry? e3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#8,<e3=int64#13
+# asm 2: adc <mulr7=%r10,<e3=%r15
+adc %r10,%r15
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#4
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rcx
+imulq $38,%rax,%rcx
+
+# qhasm: carry? e0 += mulr8
+# asm 1: add <mulr8=int64#4,<e0=int64#10
+# asm 2: add <mulr8=%rcx,<e0=%r12
+add %rcx,%r12
+
+# qhasm: carry? e1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<e1=int64#11
+# asm 2: adc <mulzero=%rdx,<e1=%r13
+adc %rdx,%r13
+
+# qhasm: carry? e2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<e2=int64#12
+# asm 2: adc <mulzero=%rdx,<e2=%r14
+adc %rdx,%r14
+
+# qhasm: carry? e3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<e3=int64#13
+# asm 2: adc <mulzero=%rdx,<e3=%r15
+adc %rdx,%r15
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: e0 += mulzero
+# asm 1: add <mulzero=int64#3,<e0=int64#10
+# asm 2: add <mulzero=%rdx,<e0=%r12
+add %rdx,%r12
+
+# qhasm: h0 = e0
+# asm 1: mov <e0=int64#10,>h0=int64#3
+# asm 2: mov <e0=%r12,>h0=%rdx
+mov %r12,%rdx
+
+# qhasm: h1 = e1
+# asm 1: mov <e1=int64#11,>h1=int64#4
+# asm 2: mov <e1=%r13,>h1=%rcx
+mov %r13,%rcx
+
+# qhasm: h2 = e2
+# asm 1: mov <e2=int64#12,>h2=int64#5
+# asm 2: mov <e2=%r14,>h2=%r8
+mov %r14,%r8
+
+# qhasm: h3 = e3
+# asm 1: mov <e3=int64#13,>h3=int64#6
+# asm 2: mov <e3=%r15,>h3=%r9
+mov %r15,%r9
+
+# qhasm: carry? e0 -= a0_stack
+# asm 1: subq <a0_stack=stack64#8,<e0=int64#10
+# asm 2: subq <a0_stack=56(%rsp),<e0=%r12
+subq 56(%rsp),%r12
+
+# qhasm: carry? e1 -= a1_stack - carry
+# asm 1: sbbq <a1_stack=stack64#9,<e1=int64#11
+# asm 2: sbbq <a1_stack=64(%rsp),<e1=%r13
+sbbq 64(%rsp),%r13
+
+# qhasm: carry? e2 -= a2_stack - carry
+# asm 1: sbbq <a2_stack=stack64#10,<e2=int64#12
+# asm 2: sbbq <a2_stack=72(%rsp),<e2=%r14
+sbbq 72(%rsp),%r14
+
+# qhasm: carry? e3 -= a3_stack - carry
+# asm 1: sbbq <a3_stack=stack64#11,<e3=int64#13
+# asm 2: sbbq <a3_stack=80(%rsp),<e3=%r15
+sbbq 80(%rsp),%r15
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#7
+# asm 2: mov $0,>subt0=%rax
+mov $0,%rax
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#8
+# asm 2: mov $38,>subt1=%r10
+mov $38,%r10
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#7,<subt1=int64#8
+# asm 2: cmovae <subt0=%rax,<subt1=%r10
+cmovae %rax,%r10
+
+# qhasm: carry? e0 -= subt1
+# asm 1: sub <subt1=int64#8,<e0=int64#10
+# asm 2: sub <subt1=%r10,<e0=%r12
+sub %r10,%r12
+
+# qhasm: carry? e1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#7,<e1=int64#11
+# asm 2: sbb <subt0=%rax,<e1=%r13
+sbb %rax,%r13
+
+# qhasm: carry? e2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#7,<e2=int64#12
+# asm 2: sbb <subt0=%rax,<e2=%r14
+sbb %rax,%r14
+
+# qhasm: carry? e3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#7,<e3=int64#13
+# asm 2: sbb <subt0=%rax,<e3=%r15
+sbb %rax,%r15
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#8,<subt0=int64#7
+# asm 2: cmovc <subt1=%r10,<subt0=%rax
+cmovc %r10,%rax
+
+# qhasm: e0 -= subt0
+# asm 1: sub <subt0=int64#7,<e0=int64#10
+# asm 2: sub <subt0=%rax,<e0=%r12
+sub %rax,%r12
+
+# qhasm: carry? h0 += a0_stack
+# asm 1: addq <a0_stack=stack64#8,<h0=int64#3
+# asm 2: addq <a0_stack=56(%rsp),<h0=%rdx
+addq 56(%rsp),%rdx
+
+# qhasm: carry? h1 += a1_stack + carry
+# asm 1: adcq <a1_stack=stack64#9,<h1=int64#4
+# asm 2: adcq <a1_stack=64(%rsp),<h1=%rcx
+adcq 64(%rsp),%rcx
+
+# qhasm: carry? h2 += a2_stack + carry
+# asm 1: adcq <a2_stack=stack64#10,<h2=int64#5
+# asm 2: adcq <a2_stack=72(%rsp),<h2=%r8
+adcq 72(%rsp),%r8
+
+# qhasm: carry? h3 += a3_stack + carry
+# asm 1: adcq <a3_stack=stack64#11,<h3=int64#6
+# asm 2: adcq <a3_stack=80(%rsp),<h3=%r9
+adcq 80(%rsp),%r9
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#7
+# asm 2: mov $0,>addt0=%rax
+mov $0,%rax
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#8
+# asm 2: mov $38,>addt1=%r10
+mov $38,%r10
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#7,<addt1=int64#8
+# asm 2: cmovae <addt0=%rax,<addt1=%r10
+cmovae %rax,%r10
+
+# qhasm: carry? h0 += addt1
+# asm 1: add <addt1=int64#8,<h0=int64#3
+# asm 2: add <addt1=%r10,<h0=%rdx
+add %r10,%rdx
+
+# qhasm: carry? h1 += addt0 + carry
+# asm 1: adc <addt0=int64#7,<h1=int64#4
+# asm 2: adc <addt0=%rax,<h1=%rcx
+adc %rax,%rcx
+
+# qhasm: carry? h2 += addt0 + carry
+# asm 1: adc <addt0=int64#7,<h2=int64#5
+# asm 2: adc <addt0=%rax,<h2=%r8
+adc %rax,%r8
+
+# qhasm: carry? h3 += addt0 + carry
+# asm 1: adc <addt0=int64#7,<h3=int64#6
+# asm 2: adc <addt0=%rax,<h3=%r9
+adc %rax,%r9
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#8,<addt0=int64#7
+# asm 2: cmovc <addt1=%r10,<addt0=%rax
+cmovc %r10,%rax
+
+# qhasm: h0 += addt0
+# asm 1: add <addt0=int64#7,<h0=int64#3
+# asm 2: add <addt0=%rax,<h0=%rdx
+add %rax,%rdx
+
+# qhasm: h0_stack = h0
+# asm 1: movq <h0=int64#3,>h0_stack=stack64#8
+# asm 2: movq <h0=%rdx,>h0_stack=56(%rsp)
+movq %rdx,56(%rsp)
+
+# qhasm: h1_stack = h1
+# asm 1: movq <h1=int64#4,>h1_stack=stack64#9
+# asm 2: movq <h1=%rcx,>h1_stack=64(%rsp)
+movq %rcx,64(%rsp)
+
+# qhasm: h2_stack = h2
+# asm 1: movq <h2=int64#5,>h2_stack=stack64#10
+# asm 2: movq <h2=%r8,>h2_stack=72(%rsp)
+movq %r8,72(%rsp)
+
+# qhasm: h3_stack = h3
+# asm 1: movq <h3=int64#6,>h3_stack=stack64#11
+# asm 2: movq <h3=%r9,>h3_stack=80(%rsp)
+movq %r9,80(%rsp)
+
+# qhasm: e0_stack = e0
+# asm 1: movq <e0=int64#10,>e0_stack=stack64#12
+# asm 2: movq <e0=%r12,>e0_stack=88(%rsp)
+movq %r12,88(%rsp)
+
+# qhasm: e1_stack = e1
+# asm 1: movq <e1=int64#11,>e1_stack=stack64#13
+# asm 2: movq <e1=%r13,>e1_stack=96(%rsp)
+movq %r13,96(%rsp)
+
+# qhasm: e2_stack = e2
+# asm 1: movq <e2=int64#12,>e2_stack=stack64#14
+# asm 2: movq <e2=%r14,>e2_stack=104(%rsp)
+movq %r14,104(%rsp)
+
+# qhasm: e3_stack = e3
+# asm 1: movq <e3=int64#13,>e3_stack=stack64#15
+# asm 2: movq <e3=%r15,>e3_stack=112(%rsp)
+movq %r15,112(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#4
+# asm 2: mov $0,>mulr4=%rcx
+mov $0,%rcx
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#5
+# asm 2: mov $0,>mulr5=%r8
+mov $0,%r8
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulx0 = *(uint64 *)(rp + 96)
+# asm 1: movq 96(<rp=int64#1),>mulx0=int64#9
+# asm 2: movq 96(<rp=%rdi),>mulx0=%r11
+movq 96(%rdi),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rsi),>mulrax=%rax
+movq 64(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: c0 = mulrax
+# asm 1: mov <mulrax=int64#7,>c0=int64#10
+# asm 2: mov <mulrax=%rax,>c0=%r12
+mov %rax,%r12
+
+# qhasm: c1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>c1=int64#11
+# asm 2: mov <mulrdx=%rdx,>c1=%r13
+mov %rdx,%r13
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rsi),>mulrax=%rax
+movq 72(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? c1 += mulrax
+# asm 1: add <mulrax=int64#7,<c1=int64#11
+# asm 2: add <mulrax=%rax,<c1=%r13
+add %rax,%r13
+
+# qhasm: c2 = 0
+# asm 1: mov $0,>c2=int64#12
+# asm 2: mov $0,>c2=%r14
+mov $0,%r14
+
+# qhasm: c2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<c2=int64#12
+# asm 2: adc <mulrdx=%rdx,<c2=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rsi),>mulrax=%rax
+movq 80(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#12
+# asm 2: add <mulrax=%rax,<c2=%r14
+add %rax,%r14
+
+# qhasm: c3 = 0
+# asm 1: mov $0,>c3=int64#13
+# asm 2: mov $0,>c3=%r15
+mov $0,%r15
+
+# qhasm: c3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<c3=int64#13
+# asm 2: adc <mulrdx=%rdx,<c3=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rsi),>mulrax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#13
+# asm 2: add <mulrax=%rax,<c3=%r15
+add %rax,%r15
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx1 = *(uint64 *)(rp + 104)
+# asm 1: movq 104(<rp=int64#1),>mulx1=int64#9
+# asm 2: movq 104(<rp=%rdi),>mulx1=%r11
+movq 104(%rdi),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rsi),>mulrax=%rax
+movq 64(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? c1 += mulrax
+# asm 1: add <mulrax=int64#7,<c1=int64#11
+# asm 2: add <mulrax=%rax,<c1=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rsi),>mulrax=%rax
+movq 72(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#12
+# asm 2: add <mulrax=%rax,<c2=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c2 += mulc
+# asm 1: add <mulc=int64#14,<c2=int64#12
+# asm 2: add <mulc=%rbx,<c2=%r14
+add %rbx,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rsi),>mulrax=%rax
+movq 80(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#13
+# asm 2: add <mulrax=%rax,<c3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c3 += mulc
+# asm 1: add <mulc=int64#14,<c3=int64#13
+# asm 2: add <mulc=%rbx,<c3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rsi),>mulrax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r8
+adc %rdx,%r8
+
+# qhasm: mulx2 = *(uint64 *)(rp + 112)
+# asm 1: movq 112(<rp=int64#1),>mulx2=int64#9
+# asm 2: movq 112(<rp=%rdi),>mulx2=%r11
+movq 112(%rdi),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rsi),>mulrax=%rax
+movq 64(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#12
+# asm 2: add <mulrax=%rax,<c2=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rsi),>mulrax=%rax
+movq 72(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#13
+# asm 2: add <mulrax=%rax,<c3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c3 += mulc
+# asm 1: add <mulc=int64#14,<c3=int64#13
+# asm 2: add <mulc=%rbx,<c3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rsi),>mulrax=%rax
+movq 80(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rsi),>mulrax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: mulx3 = *(uint64 *)(rp + 120)
+# asm 1: movq 120(<rp=int64#1),>mulx3=int64#9
+# asm 2: movq 120(<rp=%rdi),>mulx3=%r11
+movq 120(%rdi),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rsi),>mulrax=%rax
+movq 64(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#13
+# asm 2: add <mulrax=%rax,<c3=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rsi),>mulrax=%rax
+movq 72(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rsi),>mulrax=%rax
+movq 80(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rsi),>mulrax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#14,<mulr6=int64#6
+# asm 2: add <mulc=%rbx,<mulr6=%r9
+add %rbx,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr4=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#2
+# asm 2: mov <mulrax=%rax,>mulr4=%rsi
+mov %rax,%rsi
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr5=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#4
+# asm 2: mov <mulrdx=%rdx,>mulr5=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr6=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr7=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#6
+# asm 2: add <mulrax=%rax,<mulr7=%r9
+add %rax,%r9
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? c0 += mulr4
+# asm 1: add <mulr4=int64#2,<c0=int64#10
+# asm 2: add <mulr4=%rsi,<c0=%r12
+add %rsi,%r12
+
+# qhasm: carry? c1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#4,<c1=int64#11
+# asm 2: adc <mulr5=%rcx,<c1=%r13
+adc %rcx,%r13
+
+# qhasm: carry? c2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#5,<c2=int64#12
+# asm 2: adc <mulr6=%r8,<c2=%r14
+adc %r8,%r14
+
+# qhasm: carry? c3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#6,<c3=int64#13
+# asm 2: adc <mulr7=%r9,<c3=%r15
+adc %r9,%r15
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#2
+# asm 2: mov $0,>mulzero=%rsi
+mov $0,%rsi
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulr8=int64#7
+# asm 2: adc <mulzero=%rsi,<mulr8=%rax
+adc %rsi,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#3
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? c0 += mulr8
+# asm 1: add <mulr8=int64#3,<c0=int64#10
+# asm 2: add <mulr8=%rdx,<c0=%r12
+add %rdx,%r12
+
+# qhasm: carry? c1 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<c1=int64#11
+# asm 2: adc <mulzero=%rsi,<c1=%r13
+adc %rsi,%r13
+
+# qhasm: carry? c2 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<c2=int64#12
+# asm 2: adc <mulzero=%rsi,<c2=%r14
+adc %rsi,%r14
+
+# qhasm: carry? c3 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<c3=int64#13
+# asm 2: adc <mulzero=%rsi,<c3=%r15
+adc %rsi,%r15
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulzero=int64#2
+# asm 2: adc <mulzero=%rsi,<mulzero=%rsi
+adc %rsi,%rsi
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#2,>mulzero=int64#2
+# asm 2: imulq $38,<mulzero=%rsi,>mulzero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: c0 += mulzero
+# asm 1: add <mulzero=int64#2,<c0=int64#10
+# asm 2: add <mulzero=%rsi,<c0=%r12
+add %rsi,%r12
+
+# qhasm: f0 = *(uint64 *)(rp + 64)
+# asm 1: movq 64(<rp=int64#1),>f0=int64#2
+# asm 2: movq 64(<rp=%rdi),>f0=%rsi
+movq 64(%rdi),%rsi
+
+# qhasm: f1 = *(uint64 *)(rp + 72)
+# asm 1: movq 72(<rp=int64#1),>f1=int64#3
+# asm 2: movq 72(<rp=%rdi),>f1=%rdx
+movq 72(%rdi),%rdx
+
+# qhasm: f2 = *(uint64 *)(rp + 80)
+# asm 1: movq 80(<rp=int64#1),>f2=int64#4
+# asm 2: movq 80(<rp=%rdi),>f2=%rcx
+movq 80(%rdi),%rcx
+
+# qhasm: f3 = *(uint64 *)(rp + 88)
+# asm 1: movq 88(<rp=int64#1),>f3=int64#5
+# asm 2: movq 88(<rp=%rdi),>f3=%r8
+movq 88(%rdi),%r8
+
+# qhasm: carry? f0 += f0
+# asm 1: add <f0=int64#2,<f0=int64#2
+# asm 2: add <f0=%rsi,<f0=%rsi
+add %rsi,%rsi
+
+# qhasm: carry? f1 += f1 + carry
+# asm 1: adc <f1=int64#3,<f1=int64#3
+# asm 2: adc <f1=%rdx,<f1=%rdx
+adc %rdx,%rdx
+
+# qhasm: carry? f2 += f2 + carry
+# asm 1: adc <f2=int64#4,<f2=int64#4
+# asm 2: adc <f2=%rcx,<f2=%rcx
+adc %rcx,%rcx
+
+# qhasm: carry? f3 += f3 + carry
+# asm 1: adc <f3=int64#5,<f3=int64#5
+# asm 2: adc <f3=%r8,<f3=%r8
+adc %r8,%r8
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#6
+# asm 2: mov $0,>addt0=%r9
+mov $0,%r9
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#7
+# asm 2: mov $38,>addt1=%rax
+mov $38,%rax
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#6,<addt1=int64#7
+# asm 2: cmovae <addt0=%r9,<addt1=%rax
+cmovae %r9,%rax
+
+# qhasm: carry? f0 += addt1
+# asm 1: add <addt1=int64#7,<f0=int64#2
+# asm 2: add <addt1=%rax,<f0=%rsi
+add %rax,%rsi
+
+# qhasm: carry? f1 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<f1=int64#3
+# asm 2: adc <addt0=%r9,<f1=%rdx
+adc %r9,%rdx
+
+# qhasm: carry? f2 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<f2=int64#4
+# asm 2: adc <addt0=%r9,<f2=%rcx
+adc %r9,%rcx
+
+# qhasm: carry? f3 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<f3=int64#5
+# asm 2: adc <addt0=%r9,<f3=%r8
+adc %r9,%r8
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#7,<addt0=int64#6
+# asm 2: cmovc <addt1=%rax,<addt0=%r9
+cmovc %rax,%r9
+
+# qhasm: f0 += addt0
+# asm 1: add <addt0=int64#6,<f0=int64#2
+# asm 2: add <addt0=%r9,<f0=%rsi
+add %r9,%rsi
+
+# qhasm: g0 = f0
+# asm 1: mov <f0=int64#2,>g0=int64#6
+# asm 2: mov <f0=%rsi,>g0=%r9
+mov %rsi,%r9
+
+# qhasm: g1 = f1
+# asm 1: mov <f1=int64#3,>g1=int64#7
+# asm 2: mov <f1=%rdx,>g1=%rax
+mov %rdx,%rax
+
+# qhasm: g2 = f2
+# asm 1: mov <f2=int64#4,>g2=int64#8
+# asm 2: mov <f2=%rcx,>g2=%r10
+mov %rcx,%r10
+
+# qhasm: g3 = f3
+# asm 1: mov <f3=int64#5,>g3=int64#9
+# asm 2: mov <f3=%r8,>g3=%r11
+mov %r8,%r11
+
+# qhasm: carry? f0 -= c0
+# asm 1: sub <c0=int64#10,<f0=int64#2
+# asm 2: sub <c0=%r12,<f0=%rsi
+sub %r12,%rsi
+
+# qhasm: carry? f1 -= c1 - carry
+# asm 1: sbb <c1=int64#11,<f1=int64#3
+# asm 2: sbb <c1=%r13,<f1=%rdx
+sbb %r13,%rdx
+
+# qhasm: carry? f2 -= c2 - carry
+# asm 1: sbb <c2=int64#12,<f2=int64#4
+# asm 2: sbb <c2=%r14,<f2=%rcx
+sbb %r14,%rcx
+
+# qhasm: carry? f3 -= c3 - carry
+# asm 1: sbb <c3=int64#13,<f3=int64#5
+# asm 2: sbb <c3=%r15,<f3=%r8
+sbb %r15,%r8
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#14
+# asm 2: mov $0,>subt0=%rbx
+mov $0,%rbx
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#15
+# asm 2: mov $38,>subt1=%rbp
+mov $38,%rbp
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#14,<subt1=int64#15
+# asm 2: cmovae <subt0=%rbx,<subt1=%rbp
+cmovae %rbx,%rbp
+
+# qhasm: carry? f0 -= subt1
+# asm 1: sub <subt1=int64#15,<f0=int64#2
+# asm 2: sub <subt1=%rbp,<f0=%rsi
+sub %rbp,%rsi
+
+# qhasm: carry? f1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#14,<f1=int64#3
+# asm 2: sbb <subt0=%rbx,<f1=%rdx
+sbb %rbx,%rdx
+
+# qhasm: carry? f2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#14,<f2=int64#4
+# asm 2: sbb <subt0=%rbx,<f2=%rcx
+sbb %rbx,%rcx
+
+# qhasm: carry? f3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#14,<f3=int64#5
+# asm 2: sbb <subt0=%rbx,<f3=%r8
+sbb %rbx,%r8
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#15,<subt0=int64#14
+# asm 2: cmovc <subt1=%rbp,<subt0=%rbx
+cmovc %rbp,%rbx
+
+# qhasm: f0 -= subt0
+# asm 1: sub <subt0=int64#14,<f0=int64#2
+# asm 2: sub <subt0=%rbx,<f0=%rsi
+sub %rbx,%rsi
+
+# qhasm: carry? g0 += c0
+# asm 1: add <c0=int64#10,<g0=int64#6
+# asm 2: add <c0=%r12,<g0=%r9
+add %r12,%r9
+
+# qhasm: carry? g1 += c1 + carry
+# asm 1: adc <c1=int64#11,<g1=int64#7
+# asm 2: adc <c1=%r13,<g1=%rax
+adc %r13,%rax
+
+# qhasm: carry? g2 += c2 + carry
+# asm 1: adc <c2=int64#12,<g2=int64#8
+# asm 2: adc <c2=%r14,<g2=%r10
+adc %r14,%r10
+
+# qhasm: carry? g3 += c3 + carry
+# asm 1: adc <c3=int64#13,<g3=int64#9
+# asm 2: adc <c3=%r15,<g3=%r11
+adc %r15,%r11
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#10
+# asm 2: mov $0,>addt0=%r12
+mov $0,%r12
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#11
+# asm 2: mov $38,>addt1=%r13
+mov $38,%r13
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#10,<addt1=int64#11
+# asm 2: cmovae <addt0=%r12,<addt1=%r13
+cmovae %r12,%r13
+
+# qhasm: carry? g0 += addt1
+# asm 1: add <addt1=int64#11,<g0=int64#6
+# asm 2: add <addt1=%r13,<g0=%r9
+add %r13,%r9
+
+# qhasm: carry? g1 += addt0 + carry
+# asm 1: adc <addt0=int64#10,<g1=int64#7
+# asm 2: adc <addt0=%r12,<g1=%rax
+adc %r12,%rax
+
+# qhasm: carry? g2 += addt0 + carry
+# asm 1: adc <addt0=int64#10,<g2=int64#8
+# asm 2: adc <addt0=%r12,<g2=%r10
+adc %r12,%r10
+
+# qhasm: carry? g3 += addt0 + carry
+# asm 1: adc <addt0=int64#10,<g3=int64#9
+# asm 2: adc <addt0=%r12,<g3=%r11
+adc %r12,%r11
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#11,<addt0=int64#10
+# asm 2: cmovc <addt1=%r13,<addt0=%r12
+cmovc %r13,%r12
+
+# qhasm: g0 += addt0
+# asm 1: add <addt0=int64#10,<g0=int64#6
+# asm 2: add <addt0=%r12,<g0=%r9
+add %r12,%r9
+
+# qhasm: g0_stack = g0
+# asm 1: movq <g0=int64#6,>g0_stack=stack64#16
+# asm 2: movq <g0=%r9,>g0_stack=120(%rsp)
+movq %r9,120(%rsp)
+
+# qhasm: g1_stack = g1
+# asm 1: movq <g1=int64#7,>g1_stack=stack64#17
+# asm 2: movq <g1=%rax,>g1_stack=128(%rsp)
+movq %rax,128(%rsp)
+
+# qhasm: g2_stack = g2
+# asm 1: movq <g2=int64#8,>g2_stack=stack64#18
+# asm 2: movq <g2=%r10,>g2_stack=136(%rsp)
+movq %r10,136(%rsp)
+
+# qhasm: g3_stack = g3
+# asm 1: movq <g3=int64#9,>g3_stack=stack64#19
+# asm 2: movq <g3=%r11,>g3_stack=144(%rsp)
+movq %r11,144(%rsp)
+
+# qhasm: f0_stack = f0
+# asm 1: movq <f0=int64#2,>f0_stack=stack64#20
+# asm 2: movq <f0=%rsi,>f0_stack=152(%rsp)
+movq %rsi,152(%rsp)
+
+# qhasm: f1_stack = f1
+# asm 1: movq <f1=int64#3,>f1_stack=stack64#21
+# asm 2: movq <f1=%rdx,>f1_stack=160(%rsp)
+movq %rdx,160(%rsp)
+
+# qhasm: f2_stack = f2
+# asm 1: movq <f2=int64#4,>f2_stack=stack64#22
+# asm 2: movq <f2=%rcx,>f2_stack=168(%rsp)
+movq %rcx,168(%rsp)
+
+# qhasm: f3_stack = f3
+# asm 1: movq <f3=int64#5,>f3_stack=stack64#23
+# asm 2: movq <f3=%r8,>f3_stack=176(%rsp)
+movq %r8,176(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#2
+# asm 2: mov $0,>mulr4=%rsi
+mov $0,%rsi
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#4
+# asm 2: mov $0,>mulr5=%rcx
+mov $0,%rcx
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulx0 = e0_stack
+# asm 1: movq <e0_stack=stack64#12,>mulx0=int64#8
+# asm 2: movq <e0_stack=88(%rsp),>mulx0=%r10
+movq 88(%rsp),%r10
+
+# qhasm: mulrax = f0_stack
+# asm 1: movq <f0_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <f0_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: rx0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rx0=int64#9
+# asm 2: mov <mulrax=%rax,>rx0=%r11
+mov %rax,%r11
+
+# qhasm: rx1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rx1=int64#10
+# asm 2: mov <mulrdx=%rdx,>rx1=%r12
+mov %rdx,%r12
+
+# qhasm: mulrax = f1_stack
+# asm 1: movq <f1_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <f1_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? rx1 += mulrax
+# asm 1: add <mulrax=int64#7,<rx1=int64#10
+# asm 2: add <mulrax=%rax,<rx1=%r12
+add %rax,%r12
+
+# qhasm: rx2 = 0
+# asm 1: mov $0,>rx2=int64#11
+# asm 2: mov $0,>rx2=%r13
+mov $0,%r13
+
+# qhasm: rx2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rx2=int64#11
+# asm 2: adc <mulrdx=%rdx,<rx2=%r13
+adc %rdx,%r13
+
+# qhasm: mulrax = f2_stack
+# asm 1: movq <f2_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <f2_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#11
+# asm 2: add <mulrax=%rax,<rx2=%r13
+add %rax,%r13
+
+# qhasm: rx3 = 0
+# asm 1: mov $0,>rx3=int64#12
+# asm 2: mov $0,>rx3=%r14
+mov $0,%r14
+
+# qhasm: rx3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rx3=int64#12
+# asm 2: adc <mulrdx=%rdx,<rx3=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = f3_stack
+# asm 1: movq <f3_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <f3_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#12
+# asm 2: add <mulrax=%rax,<rx3=%r14
+add %rax,%r14
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#2
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rsi
+adc %rdx,%rsi
+
+# qhasm: mulx1 = e1_stack
+# asm 1: movq <e1_stack=stack64#13,>mulx1=int64#8
+# asm 2: movq <e1_stack=96(%rsp),>mulx1=%r10
+movq 96(%rsp),%r10
+
+# qhasm: mulrax = f0_stack
+# asm 1: movq <f0_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <f0_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? rx1 += mulrax
+# asm 1: add <mulrax=int64#7,<rx1=int64#10
+# asm 2: add <mulrax=%rax,<rx1=%r12
+add %rax,%r12
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f1_stack
+# asm 1: movq <f1_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <f1_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#11
+# asm 2: add <mulrax=%rax,<rx2=%r13
+add %rax,%r13
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx2 += mulc
+# asm 1: add <mulc=int64#13,<rx2=int64#11
+# asm 2: add <mulc=%r15,<rx2=%r13
+add %r15,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f2_stack
+# asm 1: movq <f2_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <f2_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#12
+# asm 2: add <mulrax=%rax,<rx3=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx3 += mulc
+# asm 1: add <mulc=int64#13,<rx3=int64#12
+# asm 2: add <mulc=%r15,<rx3=%r14
+add %r15,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f3_stack
+# asm 1: movq <f3_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <f3_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr5=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx2 = e2_stack
+# asm 1: movq <e2_stack=stack64#14,>mulx2=int64#8
+# asm 2: movq <e2_stack=104(%rsp),>mulx2=%r10
+movq 104(%rsp),%r10
+
+# qhasm: mulrax = f0_stack
+# asm 1: movq <f0_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <f0_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#11
+# asm 2: add <mulrax=%rax,<rx2=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f1_stack
+# asm 1: movq <f1_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <f1_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#12
+# asm 2: add <mulrax=%rax,<rx3=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx3 += mulc
+# asm 1: add <mulc=int64#13,<rx3=int64#12
+# asm 2: add <mulc=%r15,<rx3=%r14
+add %r15,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f2_stack
+# asm 1: movq <f2_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <f2_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f3_stack
+# asm 1: movq <f3_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <f3_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#13,<mulr5=int64#4
+# asm 2: add <mulc=%r15,<mulr5=%rcx
+add %r15,%rcx
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: mulx3 = e3_stack
+# asm 1: movq <e3_stack=stack64#15,>mulx3=int64#8
+# asm 2: movq <e3_stack=112(%rsp),>mulx3=%r10
+movq 112(%rsp),%r10
+
+# qhasm: mulrax = f0_stack
+# asm 1: movq <f0_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <f0_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#12
+# asm 2: add <mulrax=%rax,<rx3=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f1_stack
+# asm 1: movq <f1_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <f1_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f2_stack
+# asm 1: movq <f2_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <f2_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#13,<mulr5=int64#4
+# asm 2: add <mulc=%r15,<mulr5=%rcx
+add %r15,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f3_stack
+# asm 1: movq <f3_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <f3_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#13,<mulr6=int64#5
+# asm 2: add <mulc=%r15,<mulr6=%r8
+add %r15,%r8
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#2,>mulrax=int64#7
+# asm 2: mov <mulr4=%rsi,>mulrax=%rax
+mov %rsi,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#2
+# asm 2: mov <mulrax=%rax,>mulr4=%rsi
+mov %rax,%rsi
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr5=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#4
+# asm 2: mov <mulrdx=%rdx,>mulr5=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr6=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr7=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#6
+# asm 2: add <mulrax=%rax,<mulr7=%r9
+add %rax,%r9
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rx0 += mulr4
+# asm 1: add <mulr4=int64#2,<rx0=int64#9
+# asm 2: add <mulr4=%rsi,<rx0=%r11
+add %rsi,%r11
+
+# qhasm: carry? rx1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#4,<rx1=int64#10
+# asm 2: adc <mulr5=%rcx,<rx1=%r12
+adc %rcx,%r12
+
+# qhasm: carry? rx2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#5,<rx2=int64#11
+# asm 2: adc <mulr6=%r8,<rx2=%r13
+adc %r8,%r13
+
+# qhasm: carry? rx3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#6,<rx3=int64#12
+# asm 2: adc <mulr7=%r9,<rx3=%r14
+adc %r9,%r14
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#2
+# asm 2: mov $0,>mulzero=%rsi
+mov $0,%rsi
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulr8=int64#7
+# asm 2: adc <mulzero=%rsi,<mulr8=%rax
+adc %rsi,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#3
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? rx0 += mulr8
+# asm 1: add <mulr8=int64#3,<rx0=int64#9
+# asm 2: add <mulr8=%rdx,<rx0=%r11
+add %rdx,%r11
+
+# qhasm: carry? rx1 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rx1=int64#10
+# asm 2: adc <mulzero=%rsi,<rx1=%r12
+adc %rsi,%r12
+
+# qhasm: carry? rx2 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rx2=int64#11
+# asm 2: adc <mulzero=%rsi,<rx2=%r13
+adc %rsi,%r13
+
+# qhasm: carry? rx3 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rx3=int64#12
+# asm 2: adc <mulzero=%rsi,<rx3=%r14
+adc %rsi,%r14
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulzero=int64#2
+# asm 2: adc <mulzero=%rsi,<mulzero=%rsi
+adc %rsi,%rsi
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#2,>mulzero=int64#2
+# asm 2: imulq $38,<mulzero=%rsi,>mulzero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: rx0 += mulzero
+# asm 1: add <mulzero=int64#2,<rx0=int64#9
+# asm 2: add <mulzero=%rsi,<rx0=%r11
+add %rsi,%r11
+
+# qhasm: *(uint64 *)(rp + 0) = rx0
+# asm 1: movq <rx0=int64#9,0(<rp=int64#1)
+# asm 2: movq <rx0=%r11,0(<rp=%rdi)
+movq %r11,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = rx1
+# asm 1: movq <rx1=int64#10,8(<rp=int64#1)
+# asm 2: movq <rx1=%r12,8(<rp=%rdi)
+movq %r12,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = rx2
+# asm 1: movq <rx2=int64#11,16(<rp=int64#1)
+# asm 2: movq <rx2=%r13,16(<rp=%rdi)
+movq %r13,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = rx3
+# asm 1: movq <rx3=int64#12,24(<rp=int64#1)
+# asm 2: movq <rx3=%r14,24(<rp=%rdi)
+movq %r14,24(%rdi)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#2
+# asm 2: mov $0,>mulr4=%rsi
+mov $0,%rsi
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#4
+# asm 2: mov $0,>mulr5=%rcx
+mov $0,%rcx
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulx0 = h0_stack
+# asm 1: movq <h0_stack=stack64#8,>mulx0=int64#8
+# asm 2: movq <h0_stack=56(%rsp),>mulx0=%r10
+movq 56(%rsp),%r10
+
+# qhasm: mulrax = g0_stack
+# asm 1: movq <g0_stack=stack64#16,>mulrax=int64#7
+# asm 2: movq <g0_stack=120(%rsp),>mulrax=%rax
+movq 120(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: ry0 = mulrax
+# asm 1: mov <mulrax=int64#7,>ry0=int64#9
+# asm 2: mov <mulrax=%rax,>ry0=%r11
+mov %rax,%r11
+
+# qhasm: ry1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>ry1=int64#10
+# asm 2: mov <mulrdx=%rdx,>ry1=%r12
+mov %rdx,%r12
+
+# qhasm: mulrax = g1_stack
+# asm 1: movq <g1_stack=stack64#17,>mulrax=int64#7
+# asm 2: movq <g1_stack=128(%rsp),>mulrax=%rax
+movq 128(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? ry1 += mulrax
+# asm 1: add <mulrax=int64#7,<ry1=int64#10
+# asm 2: add <mulrax=%rax,<ry1=%r12
+add %rax,%r12
+
+# qhasm: ry2 = 0
+# asm 1: mov $0,>ry2=int64#11
+# asm 2: mov $0,>ry2=%r13
+mov $0,%r13
+
+# qhasm: ry2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<ry2=int64#11
+# asm 2: adc <mulrdx=%rdx,<ry2=%r13
+adc %rdx,%r13
+
+# qhasm: mulrax = g2_stack
+# asm 1: movq <g2_stack=stack64#18,>mulrax=int64#7
+# asm 2: movq <g2_stack=136(%rsp),>mulrax=%rax
+movq 136(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? ry2 += mulrax
+# asm 1: add <mulrax=int64#7,<ry2=int64#11
+# asm 2: add <mulrax=%rax,<ry2=%r13
+add %rax,%r13
+
+# qhasm: ry3 = 0
+# asm 1: mov $0,>ry3=int64#12
+# asm 2: mov $0,>ry3=%r14
+mov $0,%r14
+
+# qhasm: ry3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<ry3=int64#12
+# asm 2: adc <mulrdx=%rdx,<ry3=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = g3_stack
+# asm 1: movq <g3_stack=stack64#19,>mulrax=int64#7
+# asm 2: movq <g3_stack=144(%rsp),>mulrax=%rax
+movq 144(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#12
+# asm 2: add <mulrax=%rax,<ry3=%r14
+add %rax,%r14
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#2
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rsi
+adc %rdx,%rsi
+
+# qhasm: mulx1 = h1_stack
+# asm 1: movq <h1_stack=stack64#9,>mulx1=int64#8
+# asm 2: movq <h1_stack=64(%rsp),>mulx1=%r10
+movq 64(%rsp),%r10
+
+# qhasm: mulrax = g0_stack
+# asm 1: movq <g0_stack=stack64#16,>mulrax=int64#7
+# asm 2: movq <g0_stack=120(%rsp),>mulrax=%rax
+movq 120(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? ry1 += mulrax
+# asm 1: add <mulrax=int64#7,<ry1=int64#10
+# asm 2: add <mulrax=%rax,<ry1=%r12
+add %rax,%r12
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = g1_stack
+# asm 1: movq <g1_stack=stack64#17,>mulrax=int64#7
+# asm 2: movq <g1_stack=128(%rsp),>mulrax=%rax
+movq 128(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? ry2 += mulrax
+# asm 1: add <mulrax=int64#7,<ry2=int64#11
+# asm 2: add <mulrax=%rax,<ry2=%r13
+add %rax,%r13
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? ry2 += mulc
+# asm 1: add <mulc=int64#13,<ry2=int64#11
+# asm 2: add <mulc=%r15,<ry2=%r13
+add %r15,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = g2_stack
+# asm 1: movq <g2_stack=stack64#18,>mulrax=int64#7
+# asm 2: movq <g2_stack=136(%rsp),>mulrax=%rax
+movq 136(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#12
+# asm 2: add <mulrax=%rax,<ry3=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? ry3 += mulc
+# asm 1: add <mulc=int64#13,<ry3=int64#12
+# asm 2: add <mulc=%r15,<ry3=%r14
+add %r15,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = g3_stack
+# asm 1: movq <g3_stack=stack64#19,>mulrax=int64#7
+# asm 2: movq <g3_stack=144(%rsp),>mulrax=%rax
+movq 144(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr5=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx2 = h2_stack
+# asm 1: movq <h2_stack=stack64#10,>mulx2=int64#8
+# asm 2: movq <h2_stack=72(%rsp),>mulx2=%r10
+movq 72(%rsp),%r10
+
+# qhasm: mulrax = g0_stack
+# asm 1: movq <g0_stack=stack64#16,>mulrax=int64#7
+# asm 2: movq <g0_stack=120(%rsp),>mulrax=%rax
+movq 120(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? ry2 += mulrax
+# asm 1: add <mulrax=int64#7,<ry2=int64#11
+# asm 2: add <mulrax=%rax,<ry2=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = g1_stack
+# asm 1: movq <g1_stack=stack64#17,>mulrax=int64#7
+# asm 2: movq <g1_stack=128(%rsp),>mulrax=%rax
+movq 128(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#12
+# asm 2: add <mulrax=%rax,<ry3=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? ry3 += mulc
+# asm 1: add <mulc=int64#13,<ry3=int64#12
+# asm 2: add <mulc=%r15,<ry3=%r14
+add %r15,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = g2_stack
+# asm 1: movq <g2_stack=stack64#18,>mulrax=int64#7
+# asm 2: movq <g2_stack=136(%rsp),>mulrax=%rax
+movq 136(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = g3_stack
+# asm 1: movq <g3_stack=stack64#19,>mulrax=int64#7
+# asm 2: movq <g3_stack=144(%rsp),>mulrax=%rax
+movq 144(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#13,<mulr5=int64#4
+# asm 2: add <mulc=%r15,<mulr5=%rcx
+add %r15,%rcx
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: mulx3 = h3_stack
+# asm 1: movq <h3_stack=stack64#11,>mulx3=int64#8
+# asm 2: movq <h3_stack=80(%rsp),>mulx3=%r10
+movq 80(%rsp),%r10
+
+# qhasm: mulrax = g0_stack
+# asm 1: movq <g0_stack=stack64#16,>mulrax=int64#7
+# asm 2: movq <g0_stack=120(%rsp),>mulrax=%rax
+movq 120(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#12
+# asm 2: add <mulrax=%rax,<ry3=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = g1_stack
+# asm 1: movq <g1_stack=stack64#17,>mulrax=int64#7
+# asm 2: movq <g1_stack=128(%rsp),>mulrax=%rax
+movq 128(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = g2_stack
+# asm 1: movq <g2_stack=stack64#18,>mulrax=int64#7
+# asm 2: movq <g2_stack=136(%rsp),>mulrax=%rax
+movq 136(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#13,<mulr5=int64#4
+# asm 2: add <mulc=%r15,<mulr5=%rcx
+add %r15,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = g3_stack
+# asm 1: movq <g3_stack=stack64#19,>mulrax=int64#7
+# asm 2: movq <g3_stack=144(%rsp),>mulrax=%rax
+movq 144(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#13,<mulr6=int64#5
+# asm 2: add <mulc=%r15,<mulr6=%r8
+add %r15,%r8
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#2,>mulrax=int64#7
+# asm 2: mov <mulr4=%rsi,>mulrax=%rax
+mov %rsi,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#2
+# asm 2: mov <mulrax=%rax,>mulr4=%rsi
+mov %rax,%rsi
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr5=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#4
+# asm 2: mov <mulrdx=%rdx,>mulr5=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr6=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr7=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#6
+# asm 2: add <mulrax=%rax,<mulr7=%r9
+add %rax,%r9
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? ry0 += mulr4
+# asm 1: add <mulr4=int64#2,<ry0=int64#9
+# asm 2: add <mulr4=%rsi,<ry0=%r11
+add %rsi,%r11
+
+# qhasm: carry? ry1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#4,<ry1=int64#10
+# asm 2: adc <mulr5=%rcx,<ry1=%r12
+adc %rcx,%r12
+
+# qhasm: carry? ry2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#5,<ry2=int64#11
+# asm 2: adc <mulr6=%r8,<ry2=%r13
+adc %r8,%r13
+
+# qhasm: carry? ry3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#6,<ry3=int64#12
+# asm 2: adc <mulr7=%r9,<ry3=%r14
+adc %r9,%r14
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#2
+# asm 2: mov $0,>mulzero=%rsi
+mov $0,%rsi
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulr8=int64#7
+# asm 2: adc <mulzero=%rsi,<mulr8=%rax
+adc %rsi,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#3
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? ry0 += mulr8
+# asm 1: add <mulr8=int64#3,<ry0=int64#9
+# asm 2: add <mulr8=%rdx,<ry0=%r11
+add %rdx,%r11
+
+# qhasm: carry? ry1 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<ry1=int64#10
+# asm 2: adc <mulzero=%rsi,<ry1=%r12
+adc %rsi,%r12
+
+# qhasm: carry? ry2 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<ry2=int64#11
+# asm 2: adc <mulzero=%rsi,<ry2=%r13
+adc %rsi,%r13
+
+# qhasm: carry? ry3 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<ry3=int64#12
+# asm 2: adc <mulzero=%rsi,<ry3=%r14
+adc %rsi,%r14
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulzero=int64#2
+# asm 2: adc <mulzero=%rsi,<mulzero=%rsi
+adc %rsi,%rsi
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#2,>mulzero=int64#2
+# asm 2: imulq $38,<mulzero=%rsi,>mulzero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: ry0 += mulzero
+# asm 1: add <mulzero=int64#2,<ry0=int64#9
+# asm 2: add <mulzero=%rsi,<ry0=%r11
+add %rsi,%r11
+
+# qhasm: *(uint64 *)(rp + 32) = ry0
+# asm 1: movq <ry0=int64#9,32(<rp=int64#1)
+# asm 2: movq <ry0=%r11,32(<rp=%rdi)
+movq %r11,32(%rdi)
+
+# qhasm: *(uint64 *)(rp + 40) = ry1
+# asm 1: movq <ry1=int64#10,40(<rp=int64#1)
+# asm 2: movq <ry1=%r12,40(<rp=%rdi)
+movq %r12,40(%rdi)
+
+# qhasm: *(uint64 *)(rp + 48) = ry2
+# asm 1: movq <ry2=int64#11,48(<rp=int64#1)
+# asm 2: movq <ry2=%r13,48(<rp=%rdi)
+movq %r13,48(%rdi)
+
+# qhasm: *(uint64 *)(rp + 56) = ry3
+# asm 1: movq <ry3=int64#12,56(<rp=int64#1)
+# asm 2: movq <ry3=%r14,56(<rp=%rdi)
+movq %r14,56(%rdi)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#2
+# asm 2: mov $0,>mulr4=%rsi
+mov $0,%rsi
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#4
+# asm 2: mov $0,>mulr5=%rcx
+mov $0,%rcx
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulx0 = g0_stack
+# asm 1: movq <g0_stack=stack64#16,>mulx0=int64#8
+# asm 2: movq <g0_stack=120(%rsp),>mulx0=%r10
+movq 120(%rsp),%r10
+
+# qhasm: mulrax = f0_stack
+# asm 1: movq <f0_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <f0_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: rz0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rz0=int64#9
+# asm 2: mov <mulrax=%rax,>rz0=%r11
+mov %rax,%r11
+
+# qhasm: rz1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rz1=int64#10
+# asm 2: mov <mulrdx=%rdx,>rz1=%r12
+mov %rdx,%r12
+
+# qhasm: mulrax = f1_stack
+# asm 1: movq <f1_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <f1_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? rz1 += mulrax
+# asm 1: add <mulrax=int64#7,<rz1=int64#10
+# asm 2: add <mulrax=%rax,<rz1=%r12
+add %rax,%r12
+
+# qhasm: rz2 = 0
+# asm 1: mov $0,>rz2=int64#11
+# asm 2: mov $0,>rz2=%r13
+mov $0,%r13
+
+# qhasm: rz2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rz2=int64#11
+# asm 2: adc <mulrdx=%rdx,<rz2=%r13
+adc %rdx,%r13
+
+# qhasm: mulrax = f2_stack
+# asm 1: movq <f2_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <f2_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? rz2 += mulrax
+# asm 1: add <mulrax=int64#7,<rz2=int64#11
+# asm 2: add <mulrax=%rax,<rz2=%r13
+add %rax,%r13
+
+# qhasm: rz3 = 0
+# asm 1: mov $0,>rz3=int64#12
+# asm 2: mov $0,>rz3=%r14
+mov $0,%r14
+
+# qhasm: rz3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rz3=int64#12
+# asm 2: adc <mulrdx=%rdx,<rz3=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = f3_stack
+# asm 1: movq <f3_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <f3_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#12
+# asm 2: add <mulrax=%rax,<rz3=%r14
+add %rax,%r14
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#2
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rsi
+adc %rdx,%rsi
+
+# qhasm: mulx1 = g1_stack
+# asm 1: movq <g1_stack=stack64#17,>mulx1=int64#8
+# asm 2: movq <g1_stack=128(%rsp),>mulx1=%r10
+movq 128(%rsp),%r10
+
+# qhasm: mulrax = f0_stack
+# asm 1: movq <f0_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <f0_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? rz1 += mulrax
+# asm 1: add <mulrax=int64#7,<rz1=int64#10
+# asm 2: add <mulrax=%rax,<rz1=%r12
+add %rax,%r12
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f1_stack
+# asm 1: movq <f1_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <f1_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? rz2 += mulrax
+# asm 1: add <mulrax=int64#7,<rz2=int64#11
+# asm 2: add <mulrax=%rax,<rz2=%r13
+add %rax,%r13
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rz2 += mulc
+# asm 1: add <mulc=int64#13,<rz2=int64#11
+# asm 2: add <mulc=%r15,<rz2=%r13
+add %r15,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f2_stack
+# asm 1: movq <f2_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <f2_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#12
+# asm 2: add <mulrax=%rax,<rz3=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rz3 += mulc
+# asm 1: add <mulc=int64#13,<rz3=int64#12
+# asm 2: add <mulc=%r15,<rz3=%r14
+add %r15,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f3_stack
+# asm 1: movq <f3_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <f3_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr5=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx2 = g2_stack
+# asm 1: movq <g2_stack=stack64#18,>mulx2=int64#8
+# asm 2: movq <g2_stack=136(%rsp),>mulx2=%r10
+movq 136(%rsp),%r10
+
+# qhasm: mulrax = f0_stack
+# asm 1: movq <f0_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <f0_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? rz2 += mulrax
+# asm 1: add <mulrax=int64#7,<rz2=int64#11
+# asm 2: add <mulrax=%rax,<rz2=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f1_stack
+# asm 1: movq <f1_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <f1_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#12
+# asm 2: add <mulrax=%rax,<rz3=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rz3 += mulc
+# asm 1: add <mulc=int64#13,<rz3=int64#12
+# asm 2: add <mulc=%r15,<rz3=%r14
+add %r15,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f2_stack
+# asm 1: movq <f2_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <f2_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f3_stack
+# asm 1: movq <f3_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <f3_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#13,<mulr5=int64#4
+# asm 2: add <mulc=%r15,<mulr5=%rcx
+add %r15,%rcx
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: mulx3 = g3_stack
+# asm 1: movq <g3_stack=stack64#19,>mulx3=int64#8
+# asm 2: movq <g3_stack=144(%rsp),>mulx3=%r10
+movq 144(%rsp),%r10
+
+# qhasm: mulrax = f0_stack
+# asm 1: movq <f0_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <f0_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#12
+# asm 2: add <mulrax=%rax,<rz3=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f1_stack
+# asm 1: movq <f1_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <f1_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f2_stack
+# asm 1: movq <f2_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <f2_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#13,<mulr5=int64#4
+# asm 2: add <mulc=%r15,<mulr5=%rcx
+add %r15,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f3_stack
+# asm 1: movq <f3_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <f3_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#13,<mulr6=int64#5
+# asm 2: add <mulc=%r15,<mulr6=%r8
+add %r15,%r8
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#2,>mulrax=int64#7
+# asm 2: mov <mulr4=%rsi,>mulrax=%rax
+mov %rsi,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#2
+# asm 2: mov <mulrax=%rax,>mulr4=%rsi
+mov %rax,%rsi
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr5=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#4
+# asm 2: mov <mulrdx=%rdx,>mulr5=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr6=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr7=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#6
+# asm 2: add <mulrax=%rax,<mulr7=%r9
+add %rax,%r9
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rz0 += mulr4
+# asm 1: add <mulr4=int64#2,<rz0=int64#9
+# asm 2: add <mulr4=%rsi,<rz0=%r11
+add %rsi,%r11
+
+# qhasm: carry? rz1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#4,<rz1=int64#10
+# asm 2: adc <mulr5=%rcx,<rz1=%r12
+adc %rcx,%r12
+
+# qhasm: carry? rz2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#5,<rz2=int64#11
+# asm 2: adc <mulr6=%r8,<rz2=%r13
+adc %r8,%r13
+
+# qhasm: carry? rz3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#6,<rz3=int64#12
+# asm 2: adc <mulr7=%r9,<rz3=%r14
+adc %r9,%r14
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#2
+# asm 2: mov $0,>mulzero=%rsi
+mov $0,%rsi
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulr8=int64#7
+# asm 2: adc <mulzero=%rsi,<mulr8=%rax
+adc %rsi,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#3
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? rz0 += mulr8
+# asm 1: add <mulr8=int64#3,<rz0=int64#9
+# asm 2: add <mulr8=%rdx,<rz0=%r11
+add %rdx,%r11
+
+# qhasm: carry? rz1 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rz1=int64#10
+# asm 2: adc <mulzero=%rsi,<rz1=%r12
+adc %rsi,%r12
+
+# qhasm: carry? rz2 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rz2=int64#11
+# asm 2: adc <mulzero=%rsi,<rz2=%r13
+adc %rsi,%r13
+
+# qhasm: carry? rz3 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rz3=int64#12
+# asm 2: adc <mulzero=%rsi,<rz3=%r14
+adc %rsi,%r14
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulzero=int64#2
+# asm 2: adc <mulzero=%rsi,<mulzero=%rsi
+adc %rsi,%rsi
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#2,>mulzero=int64#2
+# asm 2: imulq $38,<mulzero=%rsi,>mulzero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: rz0 += mulzero
+# asm 1: add <mulzero=int64#2,<rz0=int64#9
+# asm 2: add <mulzero=%rsi,<rz0=%r11
+add %rsi,%r11
+
+# qhasm: *(uint64 *)(rp + 64) = rz0
+# asm 1: movq <rz0=int64#9,64(<rp=int64#1)
+# asm 2: movq <rz0=%r11,64(<rp=%rdi)
+movq %r11,64(%rdi)
+
+# qhasm: *(uint64 *)(rp + 72) = rz1
+# asm 1: movq <rz1=int64#10,72(<rp=int64#1)
+# asm 2: movq <rz1=%r12,72(<rp=%rdi)
+movq %r12,72(%rdi)
+
+# qhasm: *(uint64 *)(rp + 80) = rz2
+# asm 1: movq <rz2=int64#11,80(<rp=int64#1)
+# asm 2: movq <rz2=%r13,80(<rp=%rdi)
+movq %r13,80(%rdi)
+
+# qhasm: *(uint64 *)(rp + 88) = rz3
+# asm 1: movq <rz3=int64#12,88(<rp=int64#1)
+# asm 2: movq <rz3=%r14,88(<rp=%rdi)
+movq %r14,88(%rdi)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#2
+# asm 2: mov $0,>mulr4=%rsi
+mov $0,%rsi
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#4
+# asm 2: mov $0,>mulr5=%rcx
+mov $0,%rcx
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulx0 = e0_stack
+# asm 1: movq <e0_stack=stack64#12,>mulx0=int64#8
+# asm 2: movq <e0_stack=88(%rsp),>mulx0=%r10
+movq 88(%rsp),%r10
+
+# qhasm: mulrax = h0_stack
+# asm 1: movq <h0_stack=stack64#8,>mulrax=int64#7
+# asm 2: movq <h0_stack=56(%rsp),>mulrax=%rax
+movq 56(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: rt0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rt0=int64#9
+# asm 2: mov <mulrax=%rax,>rt0=%r11
+mov %rax,%r11
+
+# qhasm: rt1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rt1=int64#10
+# asm 2: mov <mulrdx=%rdx,>rt1=%r12
+mov %rdx,%r12
+
+# qhasm: mulrax = h1_stack
+# asm 1: movq <h1_stack=stack64#9,>mulrax=int64#7
+# asm 2: movq <h1_stack=64(%rsp),>mulrax=%rax
+movq 64(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? rt1 += mulrax
+# asm 1: add <mulrax=int64#7,<rt1=int64#10
+# asm 2: add <mulrax=%rax,<rt1=%r12
+add %rax,%r12
+
+# qhasm: rt2 = 0
+# asm 1: mov $0,>rt2=int64#11
+# asm 2: mov $0,>rt2=%r13
+mov $0,%r13
+
+# qhasm: rt2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rt2=int64#11
+# asm 2: adc <mulrdx=%rdx,<rt2=%r13
+adc %rdx,%r13
+
+# qhasm: mulrax = h2_stack
+# asm 1: movq <h2_stack=stack64#10,>mulrax=int64#7
+# asm 2: movq <h2_stack=72(%rsp),>mulrax=%rax
+movq 72(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#11
+# asm 2: add <mulrax=%rax,<rt2=%r13
+add %rax,%r13
+
+# qhasm: rt3 = 0
+# asm 1: mov $0,>rt3=int64#12
+# asm 2: mov $0,>rt3=%r14
+mov $0,%r14
+
+# qhasm: rt3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rt3=int64#12
+# asm 2: adc <mulrdx=%rdx,<rt3=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = h3_stack
+# asm 1: movq <h3_stack=stack64#11,>mulrax=int64#7
+# asm 2: movq <h3_stack=80(%rsp),>mulrax=%rax
+movq 80(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#12
+# asm 2: add <mulrax=%rax,<rt3=%r14
+add %rax,%r14
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#2
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rsi
+adc %rdx,%rsi
+
+# qhasm: mulx1 = e1_stack
+# asm 1: movq <e1_stack=stack64#13,>mulx1=int64#8
+# asm 2: movq <e1_stack=96(%rsp),>mulx1=%r10
+movq 96(%rsp),%r10
+
+# qhasm: mulrax = h0_stack
+# asm 1: movq <h0_stack=stack64#8,>mulrax=int64#7
+# asm 2: movq <h0_stack=56(%rsp),>mulrax=%rax
+movq 56(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? rt1 += mulrax
+# asm 1: add <mulrax=int64#7,<rt1=int64#10
+# asm 2: add <mulrax=%rax,<rt1=%r12
+add %rax,%r12
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = h1_stack
+# asm 1: movq <h1_stack=stack64#9,>mulrax=int64#7
+# asm 2: movq <h1_stack=64(%rsp),>mulrax=%rax
+movq 64(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#11
+# asm 2: add <mulrax=%rax,<rt2=%r13
+add %rax,%r13
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt2 += mulc
+# asm 1: add <mulc=int64#13,<rt2=int64#11
+# asm 2: add <mulc=%r15,<rt2=%r13
+add %r15,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = h2_stack
+# asm 1: movq <h2_stack=stack64#10,>mulrax=int64#7
+# asm 2: movq <h2_stack=72(%rsp),>mulrax=%rax
+movq 72(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#12
+# asm 2: add <mulrax=%rax,<rt3=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt3 += mulc
+# asm 1: add <mulc=int64#13,<rt3=int64#12
+# asm 2: add <mulc=%r15,<rt3=%r14
+add %r15,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = h3_stack
+# asm 1: movq <h3_stack=stack64#11,>mulrax=int64#7
+# asm 2: movq <h3_stack=80(%rsp),>mulrax=%rax
+movq 80(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr5=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx2 = e2_stack
+# asm 1: movq <e2_stack=stack64#14,>mulx2=int64#8
+# asm 2: movq <e2_stack=104(%rsp),>mulx2=%r10
+movq 104(%rsp),%r10
+
+# qhasm: mulrax = h0_stack
+# asm 1: movq <h0_stack=stack64#8,>mulrax=int64#7
+# asm 2: movq <h0_stack=56(%rsp),>mulrax=%rax
+movq 56(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#11
+# asm 2: add <mulrax=%rax,<rt2=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = h1_stack
+# asm 1: movq <h1_stack=stack64#9,>mulrax=int64#7
+# asm 2: movq <h1_stack=64(%rsp),>mulrax=%rax
+movq 64(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#12
+# asm 2: add <mulrax=%rax,<rt3=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt3 += mulc
+# asm 1: add <mulc=int64#13,<rt3=int64#12
+# asm 2: add <mulc=%r15,<rt3=%r14
+add %r15,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = h2_stack
+# asm 1: movq <h2_stack=stack64#10,>mulrax=int64#7
+# asm 2: movq <h2_stack=72(%rsp),>mulrax=%rax
+movq 72(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = h3_stack
+# asm 1: movq <h3_stack=stack64#11,>mulrax=int64#7
+# asm 2: movq <h3_stack=80(%rsp),>mulrax=%rax
+movq 80(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#13,<mulr5=int64#4
+# asm 2: add <mulc=%r15,<mulr5=%rcx
+add %r15,%rcx
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: mulx3 = e3_stack
+# asm 1: movq <e3_stack=stack64#15,>mulx3=int64#8
+# asm 2: movq <e3_stack=112(%rsp),>mulx3=%r10
+movq 112(%rsp),%r10
+
+# qhasm: mulrax = h0_stack
+# asm 1: movq <h0_stack=stack64#8,>mulrax=int64#7
+# asm 2: movq <h0_stack=56(%rsp),>mulrax=%rax
+movq 56(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#12
+# asm 2: add <mulrax=%rax,<rt3=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = h1_stack
+# asm 1: movq <h1_stack=stack64#9,>mulrax=int64#7
+# asm 2: movq <h1_stack=64(%rsp),>mulrax=%rax
+movq 64(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = h2_stack
+# asm 1: movq <h2_stack=stack64#10,>mulrax=int64#7
+# asm 2: movq <h2_stack=72(%rsp),>mulrax=%rax
+movq 72(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#13,<mulr5=int64#4
+# asm 2: add <mulc=%r15,<mulr5=%rcx
+add %r15,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = h3_stack
+# asm 1: movq <h3_stack=stack64#11,>mulrax=int64#7
+# asm 2: movq <h3_stack=80(%rsp),>mulrax=%rax
+movq 80(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#13,<mulr6=int64#5
+# asm 2: add <mulc=%r15,<mulr6=%r8
+add %r15,%r8
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#2,>mulrax=int64#7
+# asm 2: mov <mulr4=%rsi,>mulrax=%rax
+mov %rsi,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#2
+# asm 2: mov <mulrax=%rax,>mulr4=%rsi
+mov %rax,%rsi
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr5=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#4
+# asm 2: mov <mulrdx=%rdx,>mulr5=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr6=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr7=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#6
+# asm 2: add <mulrax=%rax,<mulr7=%r9
+add %rax,%r9
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rt0 += mulr4
+# asm 1: add <mulr4=int64#2,<rt0=int64#9
+# asm 2: add <mulr4=%rsi,<rt0=%r11
+add %rsi,%r11
+
+# qhasm: carry? rt1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#4,<rt1=int64#10
+# asm 2: adc <mulr5=%rcx,<rt1=%r12
+adc %rcx,%r12
+
+# qhasm: carry? rt2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#5,<rt2=int64#11
+# asm 2: adc <mulr6=%r8,<rt2=%r13
+adc %r8,%r13
+
+# qhasm: carry? rt3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#6,<rt3=int64#12
+# asm 2: adc <mulr7=%r9,<rt3=%r14
+adc %r9,%r14
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#2
+# asm 2: mov $0,>mulzero=%rsi
+mov $0,%rsi
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulr8=int64#7
+# asm 2: adc <mulzero=%rsi,<mulr8=%rax
+adc %rsi,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#3
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? rt0 += mulr8
+# asm 1: add <mulr8=int64#3,<rt0=int64#9
+# asm 2: add <mulr8=%rdx,<rt0=%r11
+add %rdx,%r11
+
+# qhasm: carry? rt1 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt1=int64#10
+# asm 2: adc <mulzero=%rsi,<rt1=%r12
+adc %rsi,%r12
+
+# qhasm: carry? rt2 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt2=int64#11
+# asm 2: adc <mulzero=%rsi,<rt2=%r13
+adc %rsi,%r13
+
+# qhasm: carry? rt3 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt3=int64#12
+# asm 2: adc <mulzero=%rsi,<rt3=%r14
+adc %rsi,%r14
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulzero=int64#2
+# asm 2: adc <mulzero=%rsi,<mulzero=%rsi
+adc %rsi,%rsi
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#2,>mulzero=int64#2
+# asm 2: imulq $38,<mulzero=%rsi,>mulzero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: rt0 += mulzero
+# asm 1: add <mulzero=int64#2,<rt0=int64#9
+# asm 2: add <mulzero=%rsi,<rt0=%r11
+add %rsi,%r11
+
+# qhasm: *(uint64 *)(rp + 96) = rt0
+# asm 1: movq <rt0=int64#9,96(<rp=int64#1)
+# asm 2: movq <rt0=%r11,96(<rp=%rdi)
+movq %r11,96(%rdi)
+
+# qhasm: *(uint64 *)(rp + 104) = rt1
+# asm 1: movq <rt1=int64#10,104(<rp=int64#1)
+# asm 2: movq <rt1=%r12,104(<rp=%rdi)
+movq %r12,104(%rdi)
+
+# qhasm: *(uint64 *)(rp + 112) = rt2
+# asm 1: movq <rt2=int64#11,112(<rp=int64#1)
+# asm 2: movq <rt2=%r13,112(<rp=%rdi)
+movq %r13,112(%rdi)
+
+# qhasm: *(uint64 *)(rp + 120) = rt3
+# asm 1: movq <rt3=int64#12,120(<rp=int64#1)
+# asm 2: movq <rt3=%r14,120(<rp=%rdi)
+movq %r14,120(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/ge25519_nielsadd_p1p1.s b/ext/ed25519-amd64-asm/ge25519_nielsadd_p1p1.s
new file mode 100644
index 00000000..04e9b52b
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_nielsadd_p1p1.s
@@ -0,0 +1,3072 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 pp
+
+# qhasm: int64 qp
+
+# qhasm: input rp
+
+# qhasm: input pp
+
+# qhasm: input qp
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: int64 a0
+
+# qhasm: int64 a1
+
+# qhasm: int64 a2
+
+# qhasm: int64 a3
+
+# qhasm: stack64 a0_stack
+
+# qhasm: stack64 a1_stack
+
+# qhasm: stack64 a2_stack
+
+# qhasm: stack64 a3_stack
+
+# qhasm: int64 b0
+
+# qhasm: int64 b1
+
+# qhasm: int64 b2
+
+# qhasm: int64 b3
+
+# qhasm: stack64 b0_stack
+
+# qhasm: stack64 b1_stack
+
+# qhasm: stack64 b2_stack
+
+# qhasm: stack64 b3_stack
+
+# qhasm: int64 c0
+
+# qhasm: int64 c1
+
+# qhasm: int64 c2
+
+# qhasm: int64 c3
+
+# qhasm: stack64 c0_stack
+
+# qhasm: stack64 c1_stack
+
+# qhasm: stack64 c2_stack
+
+# qhasm: stack64 c3_stack
+
+# qhasm: int64 d0
+
+# qhasm: int64 d1
+
+# qhasm: int64 d2
+
+# qhasm: int64 d3
+
+# qhasm: stack64 d0_stack
+
+# qhasm: stack64 d1_stack
+
+# qhasm: stack64 d2_stack
+
+# qhasm: stack64 d3_stack
+
+# qhasm: int64 e0
+
+# qhasm: int64 e1
+
+# qhasm: int64 e2
+
+# qhasm: int64 e3
+
+# qhasm: stack64 e0_stack
+
+# qhasm: stack64 e1_stack
+
+# qhasm: stack64 e2_stack
+
+# qhasm: stack64 e3_stack
+
+# qhasm: int64 f0
+
+# qhasm: int64 f1
+
+# qhasm: int64 f2
+
+# qhasm: int64 f3
+
+# qhasm: stack64 f0_stack
+
+# qhasm: stack64 f1_stack
+
+# qhasm: stack64 f2_stack
+
+# qhasm: stack64 f3_stack
+
+# qhasm: int64 g0
+
+# qhasm: int64 g1
+
+# qhasm: int64 g2
+
+# qhasm: int64 g3
+
+# qhasm: stack64 g0_stack
+
+# qhasm: stack64 g1_stack
+
+# qhasm: stack64 g2_stack
+
+# qhasm: stack64 g3_stack
+
+# qhasm: int64 h0
+
+# qhasm: int64 h1
+
+# qhasm: int64 h2
+
+# qhasm: int64 h3
+
+# qhasm: stack64 h0_stack
+
+# qhasm: stack64 h1_stack
+
+# qhasm: stack64 h2_stack
+
+# qhasm: stack64 h3_stack
+
+# qhasm: int64 qt0
+
+# qhasm: int64 qt1
+
+# qhasm: int64 qt2
+
+# qhasm: int64 qt3
+
+# qhasm: stack64 qt0_stack
+
+# qhasm: stack64 qt1_stack
+
+# qhasm: stack64 qt2_stack
+
+# qhasm: stack64 qt3_stack
+
+# qhasm: int64 t10
+
+# qhasm: int64 t11
+
+# qhasm: int64 t12
+
+# qhasm: int64 t13
+
+# qhasm: stack64 t10_stack
+
+# qhasm: stack64 t11_stack
+
+# qhasm: stack64 t12_stack
+
+# qhasm: stack64 t13_stack
+
+# qhasm: int64 t20
+
+# qhasm: int64 t21
+
+# qhasm: int64 t22
+
+# qhasm: int64 t23
+
+# qhasm: stack64 t20_stack
+
+# qhasm: stack64 t21_stack
+
+# qhasm: stack64 t22_stack
+
+# qhasm: stack64 t23_stack
+
+# qhasm: int64 rx0
+
+# qhasm: int64 rx1
+
+# qhasm: int64 rx2
+
+# qhasm: int64 rx3
+
+# qhasm: int64 ry0
+
+# qhasm: int64 ry1
+
+# qhasm: int64 ry2
+
+# qhasm: int64 ry3
+
+# qhasm: int64 rz0
+
+# qhasm: int64 rz1
+
+# qhasm: int64 rz2
+
+# qhasm: int64 rz3
+
+# qhasm: int64 rt0
+
+# qhasm: int64 rt1
+
+# qhasm: int64 rt2
+
+# qhasm: int64 rt3
+
+# qhasm: int64 mulr4
+
+# qhasm: int64 mulr5
+
+# qhasm: int64 mulr6
+
+# qhasm: int64 mulr7
+
+# qhasm: int64 mulr8
+
+# qhasm: int64 mulrax
+
+# qhasm: int64 mulrdx
+
+# qhasm: int64 mulx0
+
+# qhasm: int64 mulx1
+
+# qhasm: int64 mulx2
+
+# qhasm: int64 mulx3
+
+# qhasm: int64 mulc
+
+# qhasm: int64 mulzero
+
+# qhasm: int64 muli38
+
+# qhasm: int64 addt0
+
+# qhasm: int64 addt1
+
+# qhasm: int64 subt0
+
+# qhasm: int64 subt1
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_ge25519_nielsadd_p1p1
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_ge25519_nielsadd_p1p1
+.globl crypto_sign_ed25519_amd64_64_ge25519_nielsadd_p1p1
+_crypto_sign_ed25519_amd64_64_ge25519_nielsadd_p1p1:
+crypto_sign_ed25519_amd64_64_ge25519_nielsadd_p1p1:
+mov %rsp,%r11
+and $31,%r11
+add $128,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: qp = qp
+# asm 1: mov <qp=int64#3,>qp=int64#4
+# asm 2: mov <qp=%rdx,>qp=%rcx
+mov %rdx,%rcx
+
+# qhasm: a0 = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>a0=int64#3
+# asm 2: movq 32(<pp=%rsi),>a0=%rdx
+movq 32(%rsi),%rdx
+
+# qhasm: a1 = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>a1=int64#5
+# asm 2: movq 40(<pp=%rsi),>a1=%r8
+movq 40(%rsi),%r8
+
+# qhasm: a2 = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>a2=int64#6
+# asm 2: movq 48(<pp=%rsi),>a2=%r9
+movq 48(%rsi),%r9
+
+# qhasm: a3 = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>a3=int64#7
+# asm 2: movq 56(<pp=%rsi),>a3=%rax
+movq 56(%rsi),%rax
+
+# qhasm: b0 = a0
+# asm 1: mov <a0=int64#3,>b0=int64#8
+# asm 2: mov <a0=%rdx,>b0=%r10
+mov %rdx,%r10
+
+# qhasm: b1 = a1
+# asm 1: mov <a1=int64#5,>b1=int64#9
+# asm 2: mov <a1=%r8,>b1=%r11
+mov %r8,%r11
+
+# qhasm: b2 = a2
+# asm 1: mov <a2=int64#6,>b2=int64#10
+# asm 2: mov <a2=%r9,>b2=%r12
+mov %r9,%r12
+
+# qhasm: b3 = a3
+# asm 1: mov <a3=int64#7,>b3=int64#11
+# asm 2: mov <a3=%rax,>b3=%r13
+mov %rax,%r13
+
+# qhasm: carry? a0 -= *(uint64 *) (pp + 0)
+# asm 1: subq 0(<pp=int64#2),<a0=int64#3
+# asm 2: subq 0(<pp=%rsi),<a0=%rdx
+subq 0(%rsi),%rdx
+
+# qhasm: carry? a1 -= *(uint64 *) (pp + 8) - carry
+# asm 1: sbbq 8(<pp=int64#2),<a1=int64#5
+# asm 2: sbbq 8(<pp=%rsi),<a1=%r8
+sbbq 8(%rsi),%r8
+
+# qhasm: carry? a2 -= *(uint64 *) (pp + 16) - carry
+# asm 1: sbbq 16(<pp=int64#2),<a2=int64#6
+# asm 2: sbbq 16(<pp=%rsi),<a2=%r9
+sbbq 16(%rsi),%r9
+
+# qhasm: carry? a3 -= *(uint64 *) (pp + 24) - carry
+# asm 1: sbbq 24(<pp=int64#2),<a3=int64#7
+# asm 2: sbbq 24(<pp=%rsi),<a3=%rax
+sbbq 24(%rsi),%rax
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#12
+# asm 2: mov $0,>subt0=%r14
+mov $0,%r14
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#13
+# asm 2: mov $38,>subt1=%r15
+mov $38,%r15
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#12,<subt1=int64#13
+# asm 2: cmovae <subt0=%r14,<subt1=%r15
+cmovae %r14,%r15
+
+# qhasm: carry? a0 -= subt1
+# asm 1: sub <subt1=int64#13,<a0=int64#3
+# asm 2: sub <subt1=%r15,<a0=%rdx
+sub %r15,%rdx
+
+# qhasm: carry? a1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<a1=int64#5
+# asm 2: sbb <subt0=%r14,<a1=%r8
+sbb %r14,%r8
+
+# qhasm: carry? a2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<a2=int64#6
+# asm 2: sbb <subt0=%r14,<a2=%r9
+sbb %r14,%r9
+
+# qhasm: carry? a3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<a3=int64#7
+# asm 2: sbb <subt0=%r14,<a3=%rax
+sbb %r14,%rax
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#13,<subt0=int64#12
+# asm 2: cmovc <subt1=%r15,<subt0=%r14
+cmovc %r15,%r14
+
+# qhasm: a0 -= subt0
+# asm 1: sub <subt0=int64#12,<a0=int64#3
+# asm 2: sub <subt0=%r14,<a0=%rdx
+sub %r14,%rdx
+
+# qhasm: carry? b0 += *(uint64 *) (pp + 0)
+# asm 1: addq 0(<pp=int64#2),<b0=int64#8
+# asm 2: addq 0(<pp=%rsi),<b0=%r10
+addq 0(%rsi),%r10
+
+# qhasm: carry? b1 += *(uint64 *) (pp + 8) + carry
+# asm 1: adcq 8(<pp=int64#2),<b1=int64#9
+# asm 2: adcq 8(<pp=%rsi),<b1=%r11
+adcq 8(%rsi),%r11
+
+# qhasm: carry? b2 += *(uint64 *) (pp + 16) + carry
+# asm 1: adcq 16(<pp=int64#2),<b2=int64#10
+# asm 2: adcq 16(<pp=%rsi),<b2=%r12
+adcq 16(%rsi),%r12
+
+# qhasm: carry? b3 += *(uint64 *) (pp + 24) + carry
+# asm 1: adcq 24(<pp=int64#2),<b3=int64#11
+# asm 2: adcq 24(<pp=%rsi),<b3=%r13
+adcq 24(%rsi),%r13
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#12
+# asm 2: mov $0,>addt0=%r14
+mov $0,%r14
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#13
+# asm 2: mov $38,>addt1=%r15
+mov $38,%r15
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#12,<addt1=int64#13
+# asm 2: cmovae <addt0=%r14,<addt1=%r15
+cmovae %r14,%r15
+
+# qhasm: carry? b0 += addt1
+# asm 1: add <addt1=int64#13,<b0=int64#8
+# asm 2: add <addt1=%r15,<b0=%r10
+add %r15,%r10
+
+# qhasm: carry? b1 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<b1=int64#9
+# asm 2: adc <addt0=%r14,<b1=%r11
+adc %r14,%r11
+
+# qhasm: carry? b2 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<b2=int64#10
+# asm 2: adc <addt0=%r14,<b2=%r12
+adc %r14,%r12
+
+# qhasm: carry? b3 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<b3=int64#11
+# asm 2: adc <addt0=%r14,<b3=%r13
+adc %r14,%r13
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#13,<addt0=int64#12
+# asm 2: cmovc <addt1=%r15,<addt0=%r14
+cmovc %r15,%r14
+
+# qhasm: b0 += addt0
+# asm 1: add <addt0=int64#12,<b0=int64#8
+# asm 2: add <addt0=%r14,<b0=%r10
+add %r14,%r10
+
+# qhasm: a0_stack = a0
+# asm 1: movq <a0=int64#3,>a0_stack=stack64#8
+# asm 2: movq <a0=%rdx,>a0_stack=56(%rsp)
+movq %rdx,56(%rsp)
+
+# qhasm: a1_stack = a1
+# asm 1: movq <a1=int64#5,>a1_stack=stack64#9
+# asm 2: movq <a1=%r8,>a1_stack=64(%rsp)
+movq %r8,64(%rsp)
+
+# qhasm: a2_stack = a2
+# asm 1: movq <a2=int64#6,>a2_stack=stack64#10
+# asm 2: movq <a2=%r9,>a2_stack=72(%rsp)
+movq %r9,72(%rsp)
+
+# qhasm: a3_stack = a3
+# asm 1: movq <a3=int64#7,>a3_stack=stack64#11
+# asm 2: movq <a3=%rax,>a3_stack=80(%rsp)
+movq %rax,80(%rsp)
+
+# qhasm: b0_stack = b0
+# asm 1: movq <b0=int64#8,>b0_stack=stack64#12
+# asm 2: movq <b0=%r10,>b0_stack=88(%rsp)
+movq %r10,88(%rsp)
+
+# qhasm: b1_stack = b1
+# asm 1: movq <b1=int64#9,>b1_stack=stack64#13
+# asm 2: movq <b1=%r11,>b1_stack=96(%rsp)
+movq %r11,96(%rsp)
+
+# qhasm: b2_stack = b2
+# asm 1: movq <b2=int64#10,>b2_stack=stack64#14
+# asm 2: movq <b2=%r12,>b2_stack=104(%rsp)
+movq %r12,104(%rsp)
+
+# qhasm: b3_stack = b3
+# asm 1: movq <b3=int64#11,>b3_stack=stack64#15
+# asm 2: movq <b3=%r13,>b3_stack=112(%rsp)
+movq %r13,112(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = a0_stack
+# asm 1: movq <a0_stack=stack64#8,>mulx0=int64#10
+# asm 2: movq <a0_stack=56(%rsp),>mulx0=%r12
+movq 56(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: a0 = mulrax
+# asm 1: mov <mulrax=int64#7,>a0=int64#11
+# asm 2: mov <mulrax=%rax,>a0=%r13
+mov %rax,%r13
+
+# qhasm: a1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>a1=int64#12
+# asm 2: mov <mulrdx=%rdx,>a1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? a1 += mulrax
+# asm 1: add <mulrax=int64#7,<a1=int64#12
+# asm 2: add <mulrax=%rax,<a1=%r14
+add %rax,%r14
+
+# qhasm: a2 = 0
+# asm 1: mov $0,>a2=int64#13
+# asm 2: mov $0,>a2=%r15
+mov $0,%r15
+
+# qhasm: a2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<a2=int64#13
+# asm 2: adc <mulrdx=%rdx,<a2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#13
+# asm 2: add <mulrax=%rax,<a2=%r15
+add %rax,%r15
+
+# qhasm: a3 = 0
+# asm 1: mov $0,>a3=int64#14
+# asm 2: mov $0,>a3=%rbx
+mov $0,%rbx
+
+# qhasm: a3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<a3=int64#14
+# asm 2: adc <mulrdx=%rdx,<a3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = a1_stack
+# asm 1: movq <a1_stack=stack64#9,>mulx1=int64#10
+# asm 2: movq <a1_stack=64(%rsp),>mulx1=%r12
+movq 64(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? a1 += mulrax
+# asm 1: add <mulrax=int64#7,<a1=int64#12
+# asm 2: add <mulrax=%rax,<a1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#13
+# asm 2: add <mulrax=%rax,<a2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a2 += mulc
+# asm 1: add <mulc=int64#15,<a2=int64#13
+# asm 2: add <mulc=%rbp,<a2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a3 += mulc
+# asm 1: add <mulc=int64#15,<a3=int64#14
+# asm 2: add <mulc=%rbp,<a3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = a2_stack
+# asm 1: movq <a2_stack=stack64#10,>mulx2=int64#10
+# asm 2: movq <a2_stack=72(%rsp),>mulx2=%r12
+movq 72(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#13
+# asm 2: add <mulrax=%rax,<a2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a3 += mulc
+# asm 1: add <mulc=int64#15,<a3=int64#14
+# asm 2: add <mulc=%rbp,<a3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = a3_stack
+# asm 1: movq <a3_stack=stack64#11,>mulx3=int64#10
+# asm 2: movq <a3_stack=80(%rsp),>mulx3=%r12
+movq 80(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#15,<mulr6=int64#8
+# asm 2: add <mulc=%rbp,<mulr6=%r10
+add %rbp,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#5
+# asm 2: mov <mulrax=%rax,>mulr4=%r8
+mov %rax,%r8
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#6
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r9
+mov %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#9
+# asm 2: add <mulrax=%rax,<mulr7=%r11
+add %rax,%r11
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? a0 += mulr4
+# asm 1: add <mulr4=int64#5,<a0=int64#11
+# asm 2: add <mulr4=%r8,<a0=%r13
+add %r8,%r13
+
+# qhasm: carry? a1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#6,<a1=int64#12
+# asm 2: adc <mulr5=%r9,<a1=%r14
+adc %r9,%r14
+
+# qhasm: carry? a2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#8,<a2=int64#13
+# asm 2: adc <mulr6=%r10,<a2=%r15
+adc %r10,%r15
+
+# qhasm: carry? a3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#9,<a3=int64#14
+# asm 2: adc <mulr7=%r11,<a3=%rbx
+adc %r11,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#5
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%r8
+imulq $38,%rax,%r8
+
+# qhasm: carry? a0 += mulr8
+# asm 1: add <mulr8=int64#5,<a0=int64#11
+# asm 2: add <mulr8=%r8,<a0=%r13
+add %r8,%r13
+
+# qhasm: carry? a1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a1=int64#12
+# asm 2: adc <mulzero=%rdx,<a1=%r14
+adc %rdx,%r14
+
+# qhasm: carry? a2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a2=int64#13
+# asm 2: adc <mulzero=%rdx,<a2=%r15
+adc %rdx,%r15
+
+# qhasm: carry? a3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a3=int64#14
+# asm 2: adc <mulzero=%rdx,<a3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: a0 += mulzero
+# asm 1: add <mulzero=int64#3,<a0=int64#11
+# asm 2: add <mulzero=%rdx,<a0=%r13
+add %rdx,%r13
+
+# qhasm: a0_stack = a0
+# asm 1: movq <a0=int64#11,>a0_stack=stack64#8
+# asm 2: movq <a0=%r13,>a0_stack=56(%rsp)
+movq %r13,56(%rsp)
+
+# qhasm: a1_stack = a1
+# asm 1: movq <a1=int64#12,>a1_stack=stack64#9
+# asm 2: movq <a1=%r14,>a1_stack=64(%rsp)
+movq %r14,64(%rsp)
+
+# qhasm: a2_stack = a2
+# asm 1: movq <a2=int64#13,>a2_stack=stack64#10
+# asm 2: movq <a2=%r15,>a2_stack=72(%rsp)
+movq %r15,72(%rsp)
+
+# qhasm: a3_stack = a3
+# asm 1: movq <a3=int64#14,>a3_stack=stack64#11
+# asm 2: movq <a3=%rbx,>a3_stack=80(%rsp)
+movq %rbx,80(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = b0_stack
+# asm 1: movq <b0_stack=stack64#12,>mulx0=int64#10
+# asm 2: movq <b0_stack=88(%rsp),>mulx0=%r12
+movq 88(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rcx),>mulrax=%rax
+movq 32(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: e0 = mulrax
+# asm 1: mov <mulrax=int64#7,>e0=int64#11
+# asm 2: mov <mulrax=%rax,>e0=%r13
+mov %rax,%r13
+
+# qhasm: e1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>e1=int64#12
+# asm 2: mov <mulrdx=%rdx,>e1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rcx),>mulrax=%rax
+movq 40(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? e1 += mulrax
+# asm 1: add <mulrax=int64#7,<e1=int64#12
+# asm 2: add <mulrax=%rax,<e1=%r14
+add %rax,%r14
+
+# qhasm: e2 = 0
+# asm 1: mov $0,>e2=int64#13
+# asm 2: mov $0,>e2=%r15
+mov $0,%r15
+
+# qhasm: e2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<e2=int64#13
+# asm 2: adc <mulrdx=%rdx,<e2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rcx),>mulrax=%rax
+movq 48(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? e2 += mulrax
+# asm 1: add <mulrax=int64#7,<e2=int64#13
+# asm 2: add <mulrax=%rax,<e2=%r15
+add %rax,%r15
+
+# qhasm: e3 = 0
+# asm 1: mov $0,>e3=int64#14
+# asm 2: mov $0,>e3=%rbx
+mov $0,%rbx
+
+# qhasm: e3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<e3=int64#14
+# asm 2: adc <mulrdx=%rdx,<e3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rcx),>mulrax=%rax
+movq 56(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? e3 += mulrax
+# asm 1: add <mulrax=int64#7,<e3=int64#14
+# asm 2: add <mulrax=%rax,<e3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = b1_stack
+# asm 1: movq <b1_stack=stack64#13,>mulx1=int64#10
+# asm 2: movq <b1_stack=96(%rsp),>mulx1=%r12
+movq 96(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rcx),>mulrax=%rax
+movq 32(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? e1 += mulrax
+# asm 1: add <mulrax=int64#7,<e1=int64#12
+# asm 2: add <mulrax=%rax,<e1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rcx),>mulrax=%rax
+movq 40(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? e2 += mulrax
+# asm 1: add <mulrax=int64#7,<e2=int64#13
+# asm 2: add <mulrax=%rax,<e2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? e2 += mulc
+# asm 1: add <mulc=int64#15,<e2=int64#13
+# asm 2: add <mulc=%rbp,<e2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rcx),>mulrax=%rax
+movq 48(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? e3 += mulrax
+# asm 1: add <mulrax=int64#7,<e3=int64#14
+# asm 2: add <mulrax=%rax,<e3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? e3 += mulc
+# asm 1: add <mulc=int64#15,<e3=int64#14
+# asm 2: add <mulc=%rbp,<e3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rcx),>mulrax=%rax
+movq 56(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = b2_stack
+# asm 1: movq <b2_stack=stack64#14,>mulx2=int64#10
+# asm 2: movq <b2_stack=104(%rsp),>mulx2=%r12
+movq 104(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rcx),>mulrax=%rax
+movq 32(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? e2 += mulrax
+# asm 1: add <mulrax=int64#7,<e2=int64#13
+# asm 2: add <mulrax=%rax,<e2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rcx),>mulrax=%rax
+movq 40(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? e3 += mulrax
+# asm 1: add <mulrax=int64#7,<e3=int64#14
+# asm 2: add <mulrax=%rax,<e3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? e3 += mulc
+# asm 1: add <mulc=int64#15,<e3=int64#14
+# asm 2: add <mulc=%rbp,<e3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rcx),>mulrax=%rax
+movq 48(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rcx),>mulrax=%rax
+movq 56(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = b3_stack
+# asm 1: movq <b3_stack=stack64#15,>mulx3=int64#10
+# asm 2: movq <b3_stack=112(%rsp),>mulx3=%r12
+movq 112(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rcx),>mulrax=%rax
+movq 32(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? e3 += mulrax
+# asm 1: add <mulrax=int64#7,<e3=int64#14
+# asm 2: add <mulrax=%rax,<e3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rcx),>mulrax=%rax
+movq 40(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rcx),>mulrax=%rax
+movq 48(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rcx),>mulrax=%rax
+movq 56(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#15,<mulr6=int64#8
+# asm 2: add <mulc=%rbp,<mulr6=%r10
+add %rbp,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#5
+# asm 2: mov <mulrax=%rax,>mulr4=%r8
+mov %rax,%r8
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#6
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r9
+mov %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#9
+# asm 2: add <mulrax=%rax,<mulr7=%r11
+add %rax,%r11
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? e0 += mulr4
+# asm 1: add <mulr4=int64#5,<e0=int64#11
+# asm 2: add <mulr4=%r8,<e0=%r13
+add %r8,%r13
+
+# qhasm: carry? e1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#6,<e1=int64#12
+# asm 2: adc <mulr5=%r9,<e1=%r14
+adc %r9,%r14
+
+# qhasm: carry? e2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#8,<e2=int64#13
+# asm 2: adc <mulr6=%r10,<e2=%r15
+adc %r10,%r15
+
+# qhasm: carry? e3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#9,<e3=int64#14
+# asm 2: adc <mulr7=%r11,<e3=%rbx
+adc %r11,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#5
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%r8
+imulq $38,%rax,%r8
+
+# qhasm: carry? e0 += mulr8
+# asm 1: add <mulr8=int64#5,<e0=int64#11
+# asm 2: add <mulr8=%r8,<e0=%r13
+add %r8,%r13
+
+# qhasm: carry? e1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<e1=int64#12
+# asm 2: adc <mulzero=%rdx,<e1=%r14
+adc %rdx,%r14
+
+# qhasm: carry? e2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<e2=int64#13
+# asm 2: adc <mulzero=%rdx,<e2=%r15
+adc %rdx,%r15
+
+# qhasm: carry? e3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<e3=int64#14
+# asm 2: adc <mulzero=%rdx,<e3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: e0 += mulzero
+# asm 1: add <mulzero=int64#3,<e0=int64#11
+# asm 2: add <mulzero=%rdx,<e0=%r13
+add %rdx,%r13
+
+# qhasm: h0 = e0
+# asm 1: mov <e0=int64#11,>h0=int64#3
+# asm 2: mov <e0=%r13,>h0=%rdx
+mov %r13,%rdx
+
+# qhasm: h1 = e1
+# asm 1: mov <e1=int64#12,>h1=int64#5
+# asm 2: mov <e1=%r14,>h1=%r8
+mov %r14,%r8
+
+# qhasm: h2 = e2
+# asm 1: mov <e2=int64#13,>h2=int64#6
+# asm 2: mov <e2=%r15,>h2=%r9
+mov %r15,%r9
+
+# qhasm: h3 = e3
+# asm 1: mov <e3=int64#14,>h3=int64#7
+# asm 2: mov <e3=%rbx,>h3=%rax
+mov %rbx,%rax
+
+# qhasm: carry? e0 -= a0_stack
+# asm 1: subq <a0_stack=stack64#8,<e0=int64#11
+# asm 2: subq <a0_stack=56(%rsp),<e0=%r13
+subq 56(%rsp),%r13
+
+# qhasm: carry? e1 -= a1_stack - carry
+# asm 1: sbbq <a1_stack=stack64#9,<e1=int64#12
+# asm 2: sbbq <a1_stack=64(%rsp),<e1=%r14
+sbbq 64(%rsp),%r14
+
+# qhasm: carry? e2 -= a2_stack - carry
+# asm 1: sbbq <a2_stack=stack64#10,<e2=int64#13
+# asm 2: sbbq <a2_stack=72(%rsp),<e2=%r15
+sbbq 72(%rsp),%r15
+
+# qhasm: carry? e3 -= a3_stack - carry
+# asm 1: sbbq <a3_stack=stack64#11,<e3=int64#14
+# asm 2: sbbq <a3_stack=80(%rsp),<e3=%rbx
+sbbq 80(%rsp),%rbx
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#8
+# asm 2: mov $0,>subt0=%r10
+mov $0,%r10
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#9
+# asm 2: mov $38,>subt1=%r11
+mov $38,%r11
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#8,<subt1=int64#9
+# asm 2: cmovae <subt0=%r10,<subt1=%r11
+cmovae %r10,%r11
+
+# qhasm: carry? e0 -= subt1
+# asm 1: sub <subt1=int64#9,<e0=int64#11
+# asm 2: sub <subt1=%r11,<e0=%r13
+sub %r11,%r13
+
+# qhasm: carry? e1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#8,<e1=int64#12
+# asm 2: sbb <subt0=%r10,<e1=%r14
+sbb %r10,%r14
+
+# qhasm: carry? e2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#8,<e2=int64#13
+# asm 2: sbb <subt0=%r10,<e2=%r15
+sbb %r10,%r15
+
+# qhasm: carry? e3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#8,<e3=int64#14
+# asm 2: sbb <subt0=%r10,<e3=%rbx
+sbb %r10,%rbx
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#9,<subt0=int64#8
+# asm 2: cmovc <subt1=%r11,<subt0=%r10
+cmovc %r11,%r10
+
+# qhasm: e0 -= subt0
+# asm 1: sub <subt0=int64#8,<e0=int64#11
+# asm 2: sub <subt0=%r10,<e0=%r13
+sub %r10,%r13
+
+# qhasm: carry? h0 += a0_stack
+# asm 1: addq <a0_stack=stack64#8,<h0=int64#3
+# asm 2: addq <a0_stack=56(%rsp),<h0=%rdx
+addq 56(%rsp),%rdx
+
+# qhasm: carry? h1 += a1_stack + carry
+# asm 1: adcq <a1_stack=stack64#9,<h1=int64#5
+# asm 2: adcq <a1_stack=64(%rsp),<h1=%r8
+adcq 64(%rsp),%r8
+
+# qhasm: carry? h2 += a2_stack + carry
+# asm 1: adcq <a2_stack=stack64#10,<h2=int64#6
+# asm 2: adcq <a2_stack=72(%rsp),<h2=%r9
+adcq 72(%rsp),%r9
+
+# qhasm: carry? h3 += a3_stack + carry
+# asm 1: adcq <a3_stack=stack64#11,<h3=int64#7
+# asm 2: adcq <a3_stack=80(%rsp),<h3=%rax
+adcq 80(%rsp),%rax
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#8
+# asm 2: mov $0,>addt0=%r10
+mov $0,%r10
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#9
+# asm 2: mov $38,>addt1=%r11
+mov $38,%r11
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#8,<addt1=int64#9
+# asm 2: cmovae <addt0=%r10,<addt1=%r11
+cmovae %r10,%r11
+
+# qhasm: carry? h0 += addt1
+# asm 1: add <addt1=int64#9,<h0=int64#3
+# asm 2: add <addt1=%r11,<h0=%rdx
+add %r11,%rdx
+
+# qhasm: carry? h1 += addt0 + carry
+# asm 1: adc <addt0=int64#8,<h1=int64#5
+# asm 2: adc <addt0=%r10,<h1=%r8
+adc %r10,%r8
+
+# qhasm: carry? h2 += addt0 + carry
+# asm 1: adc <addt0=int64#8,<h2=int64#6
+# asm 2: adc <addt0=%r10,<h2=%r9
+adc %r10,%r9
+
+# qhasm: carry? h3 += addt0 + carry
+# asm 1: adc <addt0=int64#8,<h3=int64#7
+# asm 2: adc <addt0=%r10,<h3=%rax
+adc %r10,%rax
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#9,<addt0=int64#8
+# asm 2: cmovc <addt1=%r11,<addt0=%r10
+cmovc %r11,%r10
+
+# qhasm: h0 += addt0
+# asm 1: add <addt0=int64#8,<h0=int64#3
+# asm 2: add <addt0=%r10,<h0=%rdx
+add %r10,%rdx
+
+# qhasm: *(uint64 *)(rp + 64) = h0
+# asm 1: movq <h0=int64#3,64(<rp=int64#1)
+# asm 2: movq <h0=%rdx,64(<rp=%rdi)
+movq %rdx,64(%rdi)
+
+# qhasm: *(uint64 *)(rp + 72) = h1
+# asm 1: movq <h1=int64#5,72(<rp=int64#1)
+# asm 2: movq <h1=%r8,72(<rp=%rdi)
+movq %r8,72(%rdi)
+
+# qhasm: *(uint64 *)(rp + 80) = h2
+# asm 1: movq <h2=int64#6,80(<rp=int64#1)
+# asm 2: movq <h2=%r9,80(<rp=%rdi)
+movq %r9,80(%rdi)
+
+# qhasm: *(uint64 *)(rp + 88) = h3
+# asm 1: movq <h3=int64#7,88(<rp=int64#1)
+# asm 2: movq <h3=%rax,88(<rp=%rdi)
+movq %rax,88(%rdi)
+
+# qhasm: *(uint64 *)(rp + 0) = e0
+# asm 1: movq <e0=int64#11,0(<rp=int64#1)
+# asm 2: movq <e0=%r13,0(<rp=%rdi)
+movq %r13,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = e1
+# asm 1: movq <e1=int64#12,8(<rp=int64#1)
+# asm 2: movq <e1=%r14,8(<rp=%rdi)
+movq %r14,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = e2
+# asm 1: movq <e2=int64#13,16(<rp=int64#1)
+# asm 2: movq <e2=%r15,16(<rp=%rdi)
+movq %r15,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = e3
+# asm 1: movq <e3=int64#14,24(<rp=int64#1)
+# asm 2: movq <e3=%rbx,24(<rp=%rdi)
+movq %rbx,24(%rdi)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulx0=int64#10
+# asm 2: movq 96(<pp=%rsi),>mulx0=%r12
+movq 96(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: c0 = mulrax
+# asm 1: mov <mulrax=int64#7,>c0=int64#11
+# asm 2: mov <mulrax=%rax,>c0=%r13
+mov %rax,%r13
+
+# qhasm: c1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>c1=int64#12
+# asm 2: mov <mulrdx=%rdx,>c1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c1 += mulrax
+# asm 1: add <mulrax=int64#7,<c1=int64#12
+# asm 2: add <mulrax=%rax,<c1=%r14
+add %rax,%r14
+
+# qhasm: c2 = 0
+# asm 1: mov $0,>c2=int64#13
+# asm 2: mov $0,>c2=%r15
+mov $0,%r15
+
+# qhasm: c2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<c2=int64#13
+# asm 2: adc <mulrdx=%rdx,<c2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: c3 = 0
+# asm 1: mov $0,>c3=int64#14
+# asm 2: mov $0,>c3=%rbx
+mov $0,%rbx
+
+# qhasm: c3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<c3=int64#14
+# asm 2: adc <mulrdx=%rdx,<c3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulx1=int64#10
+# asm 2: movq 104(<pp=%rsi),>mulx1=%r12
+movq 104(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c1 += mulrax
+# asm 1: add <mulrax=int64#7,<c1=int64#12
+# asm 2: add <mulrax=%rax,<c1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c2 += mulc
+# asm 1: add <mulc=int64#15,<c2=int64#13
+# asm 2: add <mulc=%rbp,<c2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c3 += mulc
+# asm 1: add <mulc=int64#15,<c3=int64#14
+# asm 2: add <mulc=%rbp,<c3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulx2=int64#10
+# asm 2: movq 112(<pp=%rsi),>mulx2=%r12
+movq 112(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c3 += mulc
+# asm 1: add <mulc=int64#15,<c3=int64#14
+# asm 2: add <mulc=%rbp,<c3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulx3=int64#10
+# asm 2: movq 120(<pp=%rsi),>mulx3=%r12
+movq 120(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#15,<mulr6=int64#8
+# asm 2: add <mulc=%rbp,<mulr6=%r10
+add %rbp,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#4
+# asm 2: mov <mulrax=%rax,>mulr4=%rcx
+mov %rax,%rcx
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#5
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r8
+mov %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#8
+# asm 2: add <mulrax=%rax,<mulr7=%r10
+add %rax,%r10
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? c0 += mulr4
+# asm 1: add <mulr4=int64#4,<c0=int64#11
+# asm 2: add <mulr4=%rcx,<c0=%r13
+add %rcx,%r13
+
+# qhasm: carry? c1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#5,<c1=int64#12
+# asm 2: adc <mulr5=%r8,<c1=%r14
+adc %r8,%r14
+
+# qhasm: carry? c2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#6,<c2=int64#13
+# asm 2: adc <mulr6=%r9,<c2=%r15
+adc %r9,%r15
+
+# qhasm: carry? c3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#8,<c3=int64#14
+# asm 2: adc <mulr7=%r10,<c3=%rbx
+adc %r10,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#4
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rcx
+imulq $38,%rax,%rcx
+
+# qhasm: carry? c0 += mulr8
+# asm 1: add <mulr8=int64#4,<c0=int64#11
+# asm 2: add <mulr8=%rcx,<c0=%r13
+add %rcx,%r13
+
+# qhasm: carry? c1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c1=int64#12
+# asm 2: adc <mulzero=%rdx,<c1=%r14
+adc %rdx,%r14
+
+# qhasm: carry? c2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c2=int64#13
+# asm 2: adc <mulzero=%rdx,<c2=%r15
+adc %rdx,%r15
+
+# qhasm: carry? c3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c3=int64#14
+# asm 2: adc <mulzero=%rdx,<c3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: c0 += mulzero
+# asm 1: add <mulzero=int64#3,<c0=int64#11
+# asm 2: add <mulzero=%rdx,<c0=%r13
+add %rdx,%r13
+
+# qhasm: f0 = *(uint64 *)(pp + 64)
+# asm 1: movq 64(<pp=int64#2),>f0=int64#3
+# asm 2: movq 64(<pp=%rsi),>f0=%rdx
+movq 64(%rsi),%rdx
+
+# qhasm: f1 = *(uint64 *)(pp + 72)
+# asm 1: movq 72(<pp=int64#2),>f1=int64#4
+# asm 2: movq 72(<pp=%rsi),>f1=%rcx
+movq 72(%rsi),%rcx
+
+# qhasm: f2 = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>f2=int64#5
+# asm 2: movq 80(<pp=%rsi),>f2=%r8
+movq 80(%rsi),%r8
+
+# qhasm: f3 = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>f3=int64#2
+# asm 2: movq 88(<pp=%rsi),>f3=%rsi
+movq 88(%rsi),%rsi
+
+# qhasm: carry? f0 += f0
+# asm 1: add <f0=int64#3,<f0=int64#3
+# asm 2: add <f0=%rdx,<f0=%rdx
+add %rdx,%rdx
+
+# qhasm: carry? f1 += f1 + carry
+# asm 1: adc <f1=int64#4,<f1=int64#4
+# asm 2: adc <f1=%rcx,<f1=%rcx
+adc %rcx,%rcx
+
+# qhasm: carry? f2 += f2 + carry
+# asm 1: adc <f2=int64#5,<f2=int64#5
+# asm 2: adc <f2=%r8,<f2=%r8
+adc %r8,%r8
+
+# qhasm: carry? f3 += f3 + carry
+# asm 1: adc <f3=int64#2,<f3=int64#2
+# asm 2: adc <f3=%rsi,<f3=%rsi
+adc %rsi,%rsi
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#6
+# asm 2: mov $0,>addt0=%r9
+mov $0,%r9
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#7
+# asm 2: mov $38,>addt1=%rax
+mov $38,%rax
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#6,<addt1=int64#7
+# asm 2: cmovae <addt0=%r9,<addt1=%rax
+cmovae %r9,%rax
+
+# qhasm: carry? f0 += addt1
+# asm 1: add <addt1=int64#7,<f0=int64#3
+# asm 2: add <addt1=%rax,<f0=%rdx
+add %rax,%rdx
+
+# qhasm: carry? f1 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<f1=int64#4
+# asm 2: adc <addt0=%r9,<f1=%rcx
+adc %r9,%rcx
+
+# qhasm: carry? f2 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<f2=int64#5
+# asm 2: adc <addt0=%r9,<f2=%r8
+adc %r9,%r8
+
+# qhasm: carry? f3 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<f3=int64#2
+# asm 2: adc <addt0=%r9,<f3=%rsi
+adc %r9,%rsi
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#7,<addt0=int64#6
+# asm 2: cmovc <addt1=%rax,<addt0=%r9
+cmovc %rax,%r9
+
+# qhasm: f0 += addt0
+# asm 1: add <addt0=int64#6,<f0=int64#3
+# asm 2: add <addt0=%r9,<f0=%rdx
+add %r9,%rdx
+
+# qhasm: g0 = f0
+# asm 1: mov <f0=int64#3,>g0=int64#6
+# asm 2: mov <f0=%rdx,>g0=%r9
+mov %rdx,%r9
+
+# qhasm: g1 = f1
+# asm 1: mov <f1=int64#4,>g1=int64#7
+# asm 2: mov <f1=%rcx,>g1=%rax
+mov %rcx,%rax
+
+# qhasm: g2 = f2
+# asm 1: mov <f2=int64#5,>g2=int64#8
+# asm 2: mov <f2=%r8,>g2=%r10
+mov %r8,%r10
+
+# qhasm: g3 = f3
+# asm 1: mov <f3=int64#2,>g3=int64#9
+# asm 2: mov <f3=%rsi,>g3=%r11
+mov %rsi,%r11
+
+# qhasm: carry? f0 -= c0
+# asm 1: sub <c0=int64#11,<f0=int64#3
+# asm 2: sub <c0=%r13,<f0=%rdx
+sub %r13,%rdx
+
+# qhasm: carry? f1 -= c1 - carry
+# asm 1: sbb <c1=int64#12,<f1=int64#4
+# asm 2: sbb <c1=%r14,<f1=%rcx
+sbb %r14,%rcx
+
+# qhasm: carry? f2 -= c2 - carry
+# asm 1: sbb <c2=int64#13,<f2=int64#5
+# asm 2: sbb <c2=%r15,<f2=%r8
+sbb %r15,%r8
+
+# qhasm: carry? f3 -= c3 - carry
+# asm 1: sbb <c3=int64#14,<f3=int64#2
+# asm 2: sbb <c3=%rbx,<f3=%rsi
+sbb %rbx,%rsi
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#10
+# asm 2: mov $0,>subt0=%r12
+mov $0,%r12
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#15
+# asm 2: mov $38,>subt1=%rbp
+mov $38,%rbp
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#10,<subt1=int64#15
+# asm 2: cmovae <subt0=%r12,<subt1=%rbp
+cmovae %r12,%rbp
+
+# qhasm: carry? f0 -= subt1
+# asm 1: sub <subt1=int64#15,<f0=int64#3
+# asm 2: sub <subt1=%rbp,<f0=%rdx
+sub %rbp,%rdx
+
+# qhasm: carry? f1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#10,<f1=int64#4
+# asm 2: sbb <subt0=%r12,<f1=%rcx
+sbb %r12,%rcx
+
+# qhasm: carry? f2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#10,<f2=int64#5
+# asm 2: sbb <subt0=%r12,<f2=%r8
+sbb %r12,%r8
+
+# qhasm: carry? f3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#10,<f3=int64#2
+# asm 2: sbb <subt0=%r12,<f3=%rsi
+sbb %r12,%rsi
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#15,<subt0=int64#10
+# asm 2: cmovc <subt1=%rbp,<subt0=%r12
+cmovc %rbp,%r12
+
+# qhasm: f0 -= subt0
+# asm 1: sub <subt0=int64#10,<f0=int64#3
+# asm 2: sub <subt0=%r12,<f0=%rdx
+sub %r12,%rdx
+
+# qhasm: carry? g0 += c0
+# asm 1: add <c0=int64#11,<g0=int64#6
+# asm 2: add <c0=%r13,<g0=%r9
+add %r13,%r9
+
+# qhasm: carry? g1 += c1 + carry
+# asm 1: adc <c1=int64#12,<g1=int64#7
+# asm 2: adc <c1=%r14,<g1=%rax
+adc %r14,%rax
+
+# qhasm: carry? g2 += c2 + carry
+# asm 1: adc <c2=int64#13,<g2=int64#8
+# asm 2: adc <c2=%r15,<g2=%r10
+adc %r15,%r10
+
+# qhasm: carry? g3 += c3 + carry
+# asm 1: adc <c3=int64#14,<g3=int64#9
+# asm 2: adc <c3=%rbx,<g3=%r11
+adc %rbx,%r11
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#10
+# asm 2: mov $0,>addt0=%r12
+mov $0,%r12
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#11
+# asm 2: mov $38,>addt1=%r13
+mov $38,%r13
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#10,<addt1=int64#11
+# asm 2: cmovae <addt0=%r12,<addt1=%r13
+cmovae %r12,%r13
+
+# qhasm: carry? g0 += addt1
+# asm 1: add <addt1=int64#11,<g0=int64#6
+# asm 2: add <addt1=%r13,<g0=%r9
+add %r13,%r9
+
+# qhasm: carry? g1 += addt0 + carry
+# asm 1: adc <addt0=int64#10,<g1=int64#7
+# asm 2: adc <addt0=%r12,<g1=%rax
+adc %r12,%rax
+
+# qhasm: carry? g2 += addt0 + carry
+# asm 1: adc <addt0=int64#10,<g2=int64#8
+# asm 2: adc <addt0=%r12,<g2=%r10
+adc %r12,%r10
+
+# qhasm: carry? g3 += addt0 + carry
+# asm 1: adc <addt0=int64#10,<g3=int64#9
+# asm 2: adc <addt0=%r12,<g3=%r11
+adc %r12,%r11
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#11,<addt0=int64#10
+# asm 2: cmovc <addt1=%r13,<addt0=%r12
+cmovc %r13,%r12
+
+# qhasm: g0 += addt0
+# asm 1: add <addt0=int64#10,<g0=int64#6
+# asm 2: add <addt0=%r12,<g0=%r9
+add %r12,%r9
+
+# qhasm: *(uint64 *)(rp + 32) = g0
+# asm 1: movq <g0=int64#6,32(<rp=int64#1)
+# asm 2: movq <g0=%r9,32(<rp=%rdi)
+movq %r9,32(%rdi)
+
+# qhasm: *(uint64 *)(rp + 40) = g1
+# asm 1: movq <g1=int64#7,40(<rp=int64#1)
+# asm 2: movq <g1=%rax,40(<rp=%rdi)
+movq %rax,40(%rdi)
+
+# qhasm: *(uint64 *)(rp + 48) = g2
+# asm 1: movq <g2=int64#8,48(<rp=int64#1)
+# asm 2: movq <g2=%r10,48(<rp=%rdi)
+movq %r10,48(%rdi)
+
+# qhasm: *(uint64 *)(rp + 56) = g3
+# asm 1: movq <g3=int64#9,56(<rp=int64#1)
+# asm 2: movq <g3=%r11,56(<rp=%rdi)
+movq %r11,56(%rdi)
+
+# qhasm: *(uint64 *)(rp + 96) = f0
+# asm 1: movq <f0=int64#3,96(<rp=int64#1)
+# asm 2: movq <f0=%rdx,96(<rp=%rdi)
+movq %rdx,96(%rdi)
+
+# qhasm: *(uint64 *)(rp + 104) = f1
+# asm 1: movq <f1=int64#4,104(<rp=int64#1)
+# asm 2: movq <f1=%rcx,104(<rp=%rdi)
+movq %rcx,104(%rdi)
+
+# qhasm: *(uint64 *)(rp + 112) = f2
+# asm 1: movq <f2=int64#5,112(<rp=int64#1)
+# asm 2: movq <f2=%r8,112(<rp=%rdi)
+movq %r8,112(%rdi)
+
+# qhasm: *(uint64 *)(rp + 120) = f3
+# asm 1: movq <f3=int64#2,120(<rp=int64#1)
+# asm 2: movq <f3=%rsi,120(<rp=%rdi)
+movq %rsi,120(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/ge25519_p1p1_to_p2.s b/ext/ed25519-amd64-asm/ge25519_p1p1_to_p2.s
new file mode 100644
index 00000000..c3a1bdd7
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_p1p1_to_p2.s
@@ -0,0 +1,2236 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 pp
+
+# qhasm: input rp
+
+# qhasm: input pp
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: int64 rx0
+
+# qhasm: int64 rx1
+
+# qhasm: int64 rx2
+
+# qhasm: int64 rx3
+
+# qhasm: int64 ry0
+
+# qhasm: int64 ry1
+
+# qhasm: int64 ry2
+
+# qhasm: int64 ry3
+
+# qhasm: int64 rz0
+
+# qhasm: int64 rz1
+
+# qhasm: int64 rz2
+
+# qhasm: int64 rz3
+
+# qhasm: int64 mulr4
+
+# qhasm: int64 mulr5
+
+# qhasm: int64 mulr6
+
+# qhasm: int64 mulr7
+
+# qhasm: int64 mulr8
+
+# qhasm: int64 mulrax
+
+# qhasm: int64 mulrdx
+
+# qhasm: int64 mulx0
+
+# qhasm: int64 mulx1
+
+# qhasm: int64 mulx2
+
+# qhasm: int64 mulx3
+
+# qhasm: int64 mulc
+
+# qhasm: int64 mulzero
+
+# qhasm: int64 muli38
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p2
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p2
+.globl crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p2
+_crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p2:
+crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p2:
+mov %rsp,%r11
+and $31,%r11
+add $64,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#4
+# asm 2: mov $0,>mulr4=%rcx
+mov $0,%rcx
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#5
+# asm 2: mov $0,>mulr5=%r8
+mov $0,%r8
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulx0 = *(uint64 *)(pp + 0)
+# asm 1: movq 0(<pp=int64#2),>mulx0=int64#9
+# asm 2: movq 0(<pp=%rsi),>mulx0=%r11
+movq 0(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: rx0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rx0=int64#10
+# asm 2: mov <mulrax=%rax,>rx0=%r12
+mov %rax,%r12
+
+# qhasm: rx1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rx1=int64#11
+# asm 2: mov <mulrdx=%rdx,>rx1=%r13
+mov %rdx,%r13
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rx1 += mulrax
+# asm 1: add <mulrax=int64#7,<rx1=int64#11
+# asm 2: add <mulrax=%rax,<rx1=%r13
+add %rax,%r13
+
+# qhasm: rx2 = 0
+# asm 1: mov $0,>rx2=int64#12
+# asm 2: mov $0,>rx2=%r14
+mov $0,%r14
+
+# qhasm: rx2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rx2=int64#12
+# asm 2: adc <mulrdx=%rdx,<rx2=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#12
+# asm 2: add <mulrax=%rax,<rx2=%r14
+add %rax,%r14
+
+# qhasm: rx3 = 0
+# asm 1: mov $0,>rx3=int64#13
+# asm 2: mov $0,>rx3=%r15
+mov $0,%r15
+
+# qhasm: rx3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rx3=int64#13
+# asm 2: adc <mulrdx=%rdx,<rx3=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#13
+# asm 2: add <mulrax=%rax,<rx3=%r15
+add %rax,%r15
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx1 = *(uint64 *)(pp + 8)
+# asm 1: movq 8(<pp=int64#2),>mulx1=int64#9
+# asm 2: movq 8(<pp=%rsi),>mulx1=%r11
+movq 8(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rx1 += mulrax
+# asm 1: add <mulrax=int64#7,<rx1=int64#11
+# asm 2: add <mulrax=%rax,<rx1=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#12
+# asm 2: add <mulrax=%rax,<rx2=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx2 += mulc
+# asm 1: add <mulc=int64#14,<rx2=int64#12
+# asm 2: add <mulc=%rbx,<rx2=%r14
+add %rbx,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#13
+# asm 2: add <mulrax=%rax,<rx3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx3 += mulc
+# asm 1: add <mulc=int64#14,<rx3=int64#13
+# asm 2: add <mulc=%rbx,<rx3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r8
+adc %rdx,%r8
+
+# qhasm: mulx2 = *(uint64 *)(pp + 16)
+# asm 1: movq 16(<pp=int64#2),>mulx2=int64#9
+# asm 2: movq 16(<pp=%rsi),>mulx2=%r11
+movq 16(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#12
+# asm 2: add <mulrax=%rax,<rx2=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#13
+# asm 2: add <mulrax=%rax,<rx3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx3 += mulc
+# asm 1: add <mulc=int64#14,<rx3=int64#13
+# asm 2: add <mulc=%rbx,<rx3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: mulx3 = *(uint64 *)(pp + 24)
+# asm 1: movq 24(<pp=int64#2),>mulx3=int64#9
+# asm 2: movq 24(<pp=%rsi),>mulx3=%r11
+movq 24(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#13
+# asm 2: add <mulrax=%rax,<rx3=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#14,<mulr6=int64#6
+# asm 2: add <mulc=%rbx,<mulr6=%r9
+add %rbx,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr4=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#4
+# asm 2: mov <mulrax=%rax,>mulr4=%rcx
+mov %rax,%rcx
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr5=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#5
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r8
+mov %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr6=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr7=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#8
+# asm 2: add <mulrax=%rax,<mulr7=%r10
+add %rax,%r10
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rx0 += mulr4
+# asm 1: add <mulr4=int64#4,<rx0=int64#10
+# asm 2: add <mulr4=%rcx,<rx0=%r12
+add %rcx,%r12
+
+# qhasm: carry? rx1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#5,<rx1=int64#11
+# asm 2: adc <mulr5=%r8,<rx1=%r13
+adc %r8,%r13
+
+# qhasm: carry? rx2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#6,<rx2=int64#12
+# asm 2: adc <mulr6=%r9,<rx2=%r14
+adc %r9,%r14
+
+# qhasm: carry? rx3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#8,<rx3=int64#13
+# asm 2: adc <mulr7=%r10,<rx3=%r15
+adc %r10,%r15
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#4
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rcx
+imulq $38,%rax,%rcx
+
+# qhasm: carry? rx0 += mulr8
+# asm 1: add <mulr8=int64#4,<rx0=int64#10
+# asm 2: add <mulr8=%rcx,<rx0=%r12
+add %rcx,%r12
+
+# qhasm: carry? rx1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx1=int64#11
+# asm 2: adc <mulzero=%rdx,<rx1=%r13
+adc %rdx,%r13
+
+# qhasm: carry? rx2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx2=int64#12
+# asm 2: adc <mulzero=%rdx,<rx2=%r14
+adc %rdx,%r14
+
+# qhasm: carry? rx3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx3=int64#13
+# asm 2: adc <mulzero=%rdx,<rx3=%r15
+adc %rdx,%r15
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: rx0 += mulzero
+# asm 1: add <mulzero=int64#3,<rx0=int64#10
+# asm 2: add <mulzero=%rdx,<rx0=%r12
+add %rdx,%r12
+
+# qhasm: *(uint64 *)(rp + 0) = rx0
+# asm 1: movq <rx0=int64#10,0(<rp=int64#1)
+# asm 2: movq <rx0=%r12,0(<rp=%rdi)
+movq %r12,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = rx1
+# asm 1: movq <rx1=int64#11,8(<rp=int64#1)
+# asm 2: movq <rx1=%r13,8(<rp=%rdi)
+movq %r13,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = rx2
+# asm 1: movq <rx2=int64#12,16(<rp=int64#1)
+# asm 2: movq <rx2=%r14,16(<rp=%rdi)
+movq %r14,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = rx3
+# asm 1: movq <rx3=int64#13,24(<rp=int64#1)
+# asm 2: movq <rx3=%r15,24(<rp=%rdi)
+movq %r15,24(%rdi)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#4
+# asm 2: mov $0,>mulr4=%rcx
+mov $0,%rcx
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#5
+# asm 2: mov $0,>mulr5=%r8
+mov $0,%r8
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulx0 = *(uint64 *)(pp + 64)
+# asm 1: movq 64(<pp=int64#2),>mulx0=int64#9
+# asm 2: movq 64(<pp=%rsi),>mulx0=%r11
+movq 64(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<pp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: ry0 = mulrax
+# asm 1: mov <mulrax=int64#7,>ry0=int64#10
+# asm 2: mov <mulrax=%rax,>ry0=%r12
+mov %rax,%r12
+
+# qhasm: ry1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>ry1=int64#11
+# asm 2: mov <mulrdx=%rdx,>ry1=%r13
+mov %rdx,%r13
+
+# qhasm: mulrax = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<pp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? ry1 += mulrax
+# asm 1: add <mulrax=int64#7,<ry1=int64#11
+# asm 2: add <mulrax=%rax,<ry1=%r13
+add %rax,%r13
+
+# qhasm: ry2 = 0
+# asm 1: mov $0,>ry2=int64#12
+# asm 2: mov $0,>ry2=%r14
+mov $0,%r14
+
+# qhasm: ry2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<ry2=int64#12
+# asm 2: adc <mulrdx=%rdx,<ry2=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<pp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? ry2 += mulrax
+# asm 1: add <mulrax=int64#7,<ry2=int64#12
+# asm 2: add <mulrax=%rax,<ry2=%r14
+add %rax,%r14
+
+# qhasm: ry3 = 0
+# asm 1: mov $0,>ry3=int64#13
+# asm 2: mov $0,>ry3=%r15
+mov $0,%r15
+
+# qhasm: ry3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<ry3=int64#13
+# asm 2: adc <mulrdx=%rdx,<ry3=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<pp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#13
+# asm 2: add <mulrax=%rax,<ry3=%r15
+add %rax,%r15
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx1 = *(uint64 *)(pp + 72)
+# asm 1: movq 72(<pp=int64#2),>mulx1=int64#9
+# asm 2: movq 72(<pp=%rsi),>mulx1=%r11
+movq 72(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<pp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? ry1 += mulrax
+# asm 1: add <mulrax=int64#7,<ry1=int64#11
+# asm 2: add <mulrax=%rax,<ry1=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<pp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? ry2 += mulrax
+# asm 1: add <mulrax=int64#7,<ry2=int64#12
+# asm 2: add <mulrax=%rax,<ry2=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? ry2 += mulc
+# asm 1: add <mulc=int64#14,<ry2=int64#12
+# asm 2: add <mulc=%rbx,<ry2=%r14
+add %rbx,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<pp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#13
+# asm 2: add <mulrax=%rax,<ry3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? ry3 += mulc
+# asm 1: add <mulc=int64#14,<ry3=int64#13
+# asm 2: add <mulc=%rbx,<ry3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<pp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r8
+adc %rdx,%r8
+
+# qhasm: mulx2 = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>mulx2=int64#9
+# asm 2: movq 80(<pp=%rsi),>mulx2=%r11
+movq 80(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<pp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? ry2 += mulrax
+# asm 1: add <mulrax=int64#7,<ry2=int64#12
+# asm 2: add <mulrax=%rax,<ry2=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<pp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#13
+# asm 2: add <mulrax=%rax,<ry3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? ry3 += mulc
+# asm 1: add <mulc=int64#14,<ry3=int64#13
+# asm 2: add <mulc=%rbx,<ry3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<pp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<pp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: mulx3 = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>mulx3=int64#9
+# asm 2: movq 88(<pp=%rsi),>mulx3=%r11
+movq 88(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<pp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#13
+# asm 2: add <mulrax=%rax,<ry3=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<pp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<pp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<pp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#14,<mulr6=int64#6
+# asm 2: add <mulc=%rbx,<mulr6=%r9
+add %rbx,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr4=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#4
+# asm 2: mov <mulrax=%rax,>mulr4=%rcx
+mov %rax,%rcx
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr5=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#5
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r8
+mov %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr6=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr7=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#8
+# asm 2: add <mulrax=%rax,<mulr7=%r10
+add %rax,%r10
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? ry0 += mulr4
+# asm 1: add <mulr4=int64#4,<ry0=int64#10
+# asm 2: add <mulr4=%rcx,<ry0=%r12
+add %rcx,%r12
+
+# qhasm: carry? ry1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#5,<ry1=int64#11
+# asm 2: adc <mulr5=%r8,<ry1=%r13
+adc %r8,%r13
+
+# qhasm: carry? ry2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#6,<ry2=int64#12
+# asm 2: adc <mulr6=%r9,<ry2=%r14
+adc %r9,%r14
+
+# qhasm: carry? ry3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#8,<ry3=int64#13
+# asm 2: adc <mulr7=%r10,<ry3=%r15
+adc %r10,%r15
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#4
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rcx
+imulq $38,%rax,%rcx
+
+# qhasm: carry? ry0 += mulr8
+# asm 1: add <mulr8=int64#4,<ry0=int64#10
+# asm 2: add <mulr8=%rcx,<ry0=%r12
+add %rcx,%r12
+
+# qhasm: carry? ry1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<ry1=int64#11
+# asm 2: adc <mulzero=%rdx,<ry1=%r13
+adc %rdx,%r13
+
+# qhasm: carry? ry2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<ry2=int64#12
+# asm 2: adc <mulzero=%rdx,<ry2=%r14
+adc %rdx,%r14
+
+# qhasm: carry? ry3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<ry3=int64#13
+# asm 2: adc <mulzero=%rdx,<ry3=%r15
+adc %rdx,%r15
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: ry0 += mulzero
+# asm 1: add <mulzero=int64#3,<ry0=int64#10
+# asm 2: add <mulzero=%rdx,<ry0=%r12
+add %rdx,%r12
+
+# qhasm: *(uint64 *)(rp + 32) = ry0
+# asm 1: movq <ry0=int64#10,32(<rp=int64#1)
+# asm 2: movq <ry0=%r12,32(<rp=%rdi)
+movq %r12,32(%rdi)
+
+# qhasm: *(uint64 *)(rp + 40) = ry1
+# asm 1: movq <ry1=int64#11,40(<rp=int64#1)
+# asm 2: movq <ry1=%r13,40(<rp=%rdi)
+movq %r13,40(%rdi)
+
+# qhasm: *(uint64 *)(rp + 48) = ry2
+# asm 1: movq <ry2=int64#12,48(<rp=int64#1)
+# asm 2: movq <ry2=%r14,48(<rp=%rdi)
+movq %r14,48(%rdi)
+
+# qhasm: *(uint64 *)(rp + 56) = ry3
+# asm 1: movq <ry3=int64#13,56(<rp=int64#1)
+# asm 2: movq <ry3=%r15,56(<rp=%rdi)
+movq %r15,56(%rdi)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#4
+# asm 2: mov $0,>mulr4=%rcx
+mov $0,%rcx
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#5
+# asm 2: mov $0,>mulr5=%r8
+mov $0,%r8
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulx0 = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>mulx0=int64#9
+# asm 2: movq 32(<pp=%rsi),>mulx0=%r11
+movq 32(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: rz0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rz0=int64#10
+# asm 2: mov <mulrax=%rax,>rz0=%r12
+mov %rax,%r12
+
+# qhasm: rz1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rz1=int64#11
+# asm 2: mov <mulrdx=%rdx,>rz1=%r13
+mov %rdx,%r13
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rz1 += mulrax
+# asm 1: add <mulrax=int64#7,<rz1=int64#11
+# asm 2: add <mulrax=%rax,<rz1=%r13
+add %rax,%r13
+
+# qhasm: rz2 = 0
+# asm 1: mov $0,>rz2=int64#12
+# asm 2: mov $0,>rz2=%r14
+mov $0,%r14
+
+# qhasm: rz2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rz2=int64#12
+# asm 2: adc <mulrdx=%rdx,<rz2=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rz2 += mulrax
+# asm 1: add <mulrax=int64#7,<rz2=int64#12
+# asm 2: add <mulrax=%rax,<rz2=%r14
+add %rax,%r14
+
+# qhasm: rz3 = 0
+# asm 1: mov $0,>rz3=int64#13
+# asm 2: mov $0,>rz3=%r15
+mov $0,%r15
+
+# qhasm: rz3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rz3=int64#13
+# asm 2: adc <mulrdx=%rdx,<rz3=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#13
+# asm 2: add <mulrax=%rax,<rz3=%r15
+add %rax,%r15
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx1 = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>mulx1=int64#9
+# asm 2: movq 40(<pp=%rsi),>mulx1=%r11
+movq 40(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rz1 += mulrax
+# asm 1: add <mulrax=int64#7,<rz1=int64#11
+# asm 2: add <mulrax=%rax,<rz1=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rz2 += mulrax
+# asm 1: add <mulrax=int64#7,<rz2=int64#12
+# asm 2: add <mulrax=%rax,<rz2=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rz2 += mulc
+# asm 1: add <mulc=int64#14,<rz2=int64#12
+# asm 2: add <mulc=%rbx,<rz2=%r14
+add %rbx,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#13
+# asm 2: add <mulrax=%rax,<rz3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rz3 += mulc
+# asm 1: add <mulc=int64#14,<rz3=int64#13
+# asm 2: add <mulc=%rbx,<rz3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r8
+adc %rdx,%r8
+
+# qhasm: mulx2 = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>mulx2=int64#9
+# asm 2: movq 48(<pp=%rsi),>mulx2=%r11
+movq 48(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? rz2 += mulrax
+# asm 1: add <mulrax=int64#7,<rz2=int64#12
+# asm 2: add <mulrax=%rax,<rz2=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#13
+# asm 2: add <mulrax=%rax,<rz3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rz3 += mulc
+# asm 1: add <mulc=int64#14,<rz3=int64#13
+# asm 2: add <mulc=%rbx,<rz3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: mulx3 = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>mulx3=int64#9
+# asm 2: movq 56(<pp=%rsi),>mulx3=%r11
+movq 56(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#13
+# asm 2: add <mulrax=%rax,<rz3=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#14,<mulr6=int64#6
+# asm 2: add <mulc=%rbx,<mulr6=%r9
+add %rbx,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr4=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#2
+# asm 2: mov <mulrax=%rax,>mulr4=%rsi
+mov %rax,%rsi
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr5=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#4
+# asm 2: mov <mulrdx=%rdx,>mulr5=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr6=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr7=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#6
+# asm 2: add <mulrax=%rax,<mulr7=%r9
+add %rax,%r9
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rz0 += mulr4
+# asm 1: add <mulr4=int64#2,<rz0=int64#10
+# asm 2: add <mulr4=%rsi,<rz0=%r12
+add %rsi,%r12
+
+# qhasm: carry? rz1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#4,<rz1=int64#11
+# asm 2: adc <mulr5=%rcx,<rz1=%r13
+adc %rcx,%r13
+
+# qhasm: carry? rz2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#5,<rz2=int64#12
+# asm 2: adc <mulr6=%r8,<rz2=%r14
+adc %r8,%r14
+
+# qhasm: carry? rz3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#6,<rz3=int64#13
+# asm 2: adc <mulr7=%r9,<rz3=%r15
+adc %r9,%r15
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#2
+# asm 2: mov $0,>mulzero=%rsi
+mov $0,%rsi
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulr8=int64#7
+# asm 2: adc <mulzero=%rsi,<mulr8=%rax
+adc %rsi,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#3
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? rz0 += mulr8
+# asm 1: add <mulr8=int64#3,<rz0=int64#10
+# asm 2: add <mulr8=%rdx,<rz0=%r12
+add %rdx,%r12
+
+# qhasm: carry? rz1 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rz1=int64#11
+# asm 2: adc <mulzero=%rsi,<rz1=%r13
+adc %rsi,%r13
+
+# qhasm: carry? rz2 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rz2=int64#12
+# asm 2: adc <mulzero=%rsi,<rz2=%r14
+adc %rsi,%r14
+
+# qhasm: carry? rz3 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rz3=int64#13
+# asm 2: adc <mulzero=%rsi,<rz3=%r15
+adc %rsi,%r15
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulzero=int64#2
+# asm 2: adc <mulzero=%rsi,<mulzero=%rsi
+adc %rsi,%rsi
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#2,>mulzero=int64#2
+# asm 2: imulq $38,<mulzero=%rsi,>mulzero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: rz0 += mulzero
+# asm 1: add <mulzero=int64#2,<rz0=int64#10
+# asm 2: add <mulzero=%rsi,<rz0=%r12
+add %rsi,%r12
+
+# qhasm: *(uint64 *)(rp + 64) = rz0
+# asm 1: movq <rz0=int64#10,64(<rp=int64#1)
+# asm 2: movq <rz0=%r12,64(<rp=%rdi)
+movq %r12,64(%rdi)
+
+# qhasm: *(uint64 *)(rp + 72) = rz1
+# asm 1: movq <rz1=int64#11,72(<rp=int64#1)
+# asm 2: movq <rz1=%r13,72(<rp=%rdi)
+movq %r13,72(%rdi)
+
+# qhasm: *(uint64 *)(rp + 80) = rz2
+# asm 1: movq <rz2=int64#12,80(<rp=int64#1)
+# asm 2: movq <rz2=%r14,80(<rp=%rdi)
+movq %r14,80(%rdi)
+
+# qhasm: *(uint64 *)(rp + 88) = rz3
+# asm 1: movq <rz3=int64#13,88(<rp=int64#1)
+# asm 2: movq <rz3=%r15,88(<rp=%rdi)
+movq %r15,88(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/ge25519_p1p1_to_p3.s b/ext/ed25519-amd64-asm/ge25519_p1p1_to_p3.s
new file mode 100644
index 00000000..607b9eaf
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_p1p1_to_p3.s
@@ -0,0 +1,2926 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 pp
+
+# qhasm: input rp
+
+# qhasm: input pp
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: int64 rx0
+
+# qhasm: int64 rx1
+
+# qhasm: int64 rx2
+
+# qhasm: int64 rx3
+
+# qhasm: int64 ry0
+
+# qhasm: int64 ry1
+
+# qhasm: int64 ry2
+
+# qhasm: int64 ry3
+
+# qhasm: int64 rz0
+
+# qhasm: int64 rz1
+
+# qhasm: int64 rz2
+
+# qhasm: int64 rz3
+
+# qhasm: int64 rt0
+
+# qhasm: int64 rt1
+
+# qhasm: int64 rt2
+
+# qhasm: int64 rt3
+
+# qhasm: int64 mulr4
+
+# qhasm: int64 mulr5
+
+# qhasm: int64 mulr6
+
+# qhasm: int64 mulr7
+
+# qhasm: int64 mulr8
+
+# qhasm: int64 mulrax
+
+# qhasm: int64 mulrdx
+
+# qhasm: int64 mulx0
+
+# qhasm: int64 mulx1
+
+# qhasm: int64 mulx2
+
+# qhasm: int64 mulx3
+
+# qhasm: int64 mulc
+
+# qhasm: int64 mulzero
+
+# qhasm: int64 muli38
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p3
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p3
+.globl crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p3
+_crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p3:
+crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p3:
+mov %rsp,%r11
+and $31,%r11
+add $64,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#4
+# asm 2: mov $0,>mulr4=%rcx
+mov $0,%rcx
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#5
+# asm 2: mov $0,>mulr5=%r8
+mov $0,%r8
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulx0 = *(uint64 *)(pp + 0)
+# asm 1: movq 0(<pp=int64#2),>mulx0=int64#9
+# asm 2: movq 0(<pp=%rsi),>mulx0=%r11
+movq 0(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: rx0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rx0=int64#10
+# asm 2: mov <mulrax=%rax,>rx0=%r12
+mov %rax,%r12
+
+# qhasm: rx1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rx1=int64#11
+# asm 2: mov <mulrdx=%rdx,>rx1=%r13
+mov %rdx,%r13
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rx1 += mulrax
+# asm 1: add <mulrax=int64#7,<rx1=int64#11
+# asm 2: add <mulrax=%rax,<rx1=%r13
+add %rax,%r13
+
+# qhasm: rx2 = 0
+# asm 1: mov $0,>rx2=int64#12
+# asm 2: mov $0,>rx2=%r14
+mov $0,%r14
+
+# qhasm: rx2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rx2=int64#12
+# asm 2: adc <mulrdx=%rdx,<rx2=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#12
+# asm 2: add <mulrax=%rax,<rx2=%r14
+add %rax,%r14
+
+# qhasm: rx3 = 0
+# asm 1: mov $0,>rx3=int64#13
+# asm 2: mov $0,>rx3=%r15
+mov $0,%r15
+
+# qhasm: rx3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rx3=int64#13
+# asm 2: adc <mulrdx=%rdx,<rx3=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#13
+# asm 2: add <mulrax=%rax,<rx3=%r15
+add %rax,%r15
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx1 = *(uint64 *)(pp + 8)
+# asm 1: movq 8(<pp=int64#2),>mulx1=int64#9
+# asm 2: movq 8(<pp=%rsi),>mulx1=%r11
+movq 8(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rx1 += mulrax
+# asm 1: add <mulrax=int64#7,<rx1=int64#11
+# asm 2: add <mulrax=%rax,<rx1=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#12
+# asm 2: add <mulrax=%rax,<rx2=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx2 += mulc
+# asm 1: add <mulc=int64#14,<rx2=int64#12
+# asm 2: add <mulc=%rbx,<rx2=%r14
+add %rbx,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#13
+# asm 2: add <mulrax=%rax,<rx3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx3 += mulc
+# asm 1: add <mulc=int64#14,<rx3=int64#13
+# asm 2: add <mulc=%rbx,<rx3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r8
+adc %rdx,%r8
+
+# qhasm: mulx2 = *(uint64 *)(pp + 16)
+# asm 1: movq 16(<pp=int64#2),>mulx2=int64#9
+# asm 2: movq 16(<pp=%rsi),>mulx2=%r11
+movq 16(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#12
+# asm 2: add <mulrax=%rax,<rx2=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#13
+# asm 2: add <mulrax=%rax,<rx3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx3 += mulc
+# asm 1: add <mulc=int64#14,<rx3=int64#13
+# asm 2: add <mulc=%rbx,<rx3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: mulx3 = *(uint64 *)(pp + 24)
+# asm 1: movq 24(<pp=int64#2),>mulx3=int64#9
+# asm 2: movq 24(<pp=%rsi),>mulx3=%r11
+movq 24(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#13
+# asm 2: add <mulrax=%rax,<rx3=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#14,<mulr6=int64#6
+# asm 2: add <mulc=%rbx,<mulr6=%r9
+add %rbx,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr4=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#4
+# asm 2: mov <mulrax=%rax,>mulr4=%rcx
+mov %rax,%rcx
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr5=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#5
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r8
+mov %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr6=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr7=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#8
+# asm 2: add <mulrax=%rax,<mulr7=%r10
+add %rax,%r10
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rx0 += mulr4
+# asm 1: add <mulr4=int64#4,<rx0=int64#10
+# asm 2: add <mulr4=%rcx,<rx0=%r12
+add %rcx,%r12
+
+# qhasm: carry? rx1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#5,<rx1=int64#11
+# asm 2: adc <mulr5=%r8,<rx1=%r13
+adc %r8,%r13
+
+# qhasm: carry? rx2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#6,<rx2=int64#12
+# asm 2: adc <mulr6=%r9,<rx2=%r14
+adc %r9,%r14
+
+# qhasm: carry? rx3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#8,<rx3=int64#13
+# asm 2: adc <mulr7=%r10,<rx3=%r15
+adc %r10,%r15
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#4
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rcx
+imulq $38,%rax,%rcx
+
+# qhasm: carry? rx0 += mulr8
+# asm 1: add <mulr8=int64#4,<rx0=int64#10
+# asm 2: add <mulr8=%rcx,<rx0=%r12
+add %rcx,%r12
+
+# qhasm: carry? rx1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx1=int64#11
+# asm 2: adc <mulzero=%rdx,<rx1=%r13
+adc %rdx,%r13
+
+# qhasm: carry? rx2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx2=int64#12
+# asm 2: adc <mulzero=%rdx,<rx2=%r14
+adc %rdx,%r14
+
+# qhasm: carry? rx3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx3=int64#13
+# asm 2: adc <mulzero=%rdx,<rx3=%r15
+adc %rdx,%r15
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: rx0 += mulzero
+# asm 1: add <mulzero=int64#3,<rx0=int64#10
+# asm 2: add <mulzero=%rdx,<rx0=%r12
+add %rdx,%r12
+
+# qhasm: *(uint64 *)(rp + 0) = rx0
+# asm 1: movq <rx0=int64#10,0(<rp=int64#1)
+# asm 2: movq <rx0=%r12,0(<rp=%rdi)
+movq %r12,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = rx1
+# asm 1: movq <rx1=int64#11,8(<rp=int64#1)
+# asm 2: movq <rx1=%r13,8(<rp=%rdi)
+movq %r13,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = rx2
+# asm 1: movq <rx2=int64#12,16(<rp=int64#1)
+# asm 2: movq <rx2=%r14,16(<rp=%rdi)
+movq %r14,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = rx3
+# asm 1: movq <rx3=int64#13,24(<rp=int64#1)
+# asm 2: movq <rx3=%r15,24(<rp=%rdi)
+movq %r15,24(%rdi)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#4
+# asm 2: mov $0,>mulr4=%rcx
+mov $0,%rcx
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#5
+# asm 2: mov $0,>mulr5=%r8
+mov $0,%r8
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulx0 = *(uint64 *)(pp + 64)
+# asm 1: movq 64(<pp=int64#2),>mulx0=int64#9
+# asm 2: movq 64(<pp=%rsi),>mulx0=%r11
+movq 64(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<pp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: ry0 = mulrax
+# asm 1: mov <mulrax=int64#7,>ry0=int64#10
+# asm 2: mov <mulrax=%rax,>ry0=%r12
+mov %rax,%r12
+
+# qhasm: ry1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>ry1=int64#11
+# asm 2: mov <mulrdx=%rdx,>ry1=%r13
+mov %rdx,%r13
+
+# qhasm: mulrax = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<pp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? ry1 += mulrax
+# asm 1: add <mulrax=int64#7,<ry1=int64#11
+# asm 2: add <mulrax=%rax,<ry1=%r13
+add %rax,%r13
+
+# qhasm: ry2 = 0
+# asm 1: mov $0,>ry2=int64#12
+# asm 2: mov $0,>ry2=%r14
+mov $0,%r14
+
+# qhasm: ry2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<ry2=int64#12
+# asm 2: adc <mulrdx=%rdx,<ry2=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<pp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? ry2 += mulrax
+# asm 1: add <mulrax=int64#7,<ry2=int64#12
+# asm 2: add <mulrax=%rax,<ry2=%r14
+add %rax,%r14
+
+# qhasm: ry3 = 0
+# asm 1: mov $0,>ry3=int64#13
+# asm 2: mov $0,>ry3=%r15
+mov $0,%r15
+
+# qhasm: ry3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<ry3=int64#13
+# asm 2: adc <mulrdx=%rdx,<ry3=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<pp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#13
+# asm 2: add <mulrax=%rax,<ry3=%r15
+add %rax,%r15
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx1 = *(uint64 *)(pp + 72)
+# asm 1: movq 72(<pp=int64#2),>mulx1=int64#9
+# asm 2: movq 72(<pp=%rsi),>mulx1=%r11
+movq 72(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<pp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? ry1 += mulrax
+# asm 1: add <mulrax=int64#7,<ry1=int64#11
+# asm 2: add <mulrax=%rax,<ry1=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<pp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? ry2 += mulrax
+# asm 1: add <mulrax=int64#7,<ry2=int64#12
+# asm 2: add <mulrax=%rax,<ry2=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? ry2 += mulc
+# asm 1: add <mulc=int64#14,<ry2=int64#12
+# asm 2: add <mulc=%rbx,<ry2=%r14
+add %rbx,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<pp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#13
+# asm 2: add <mulrax=%rax,<ry3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? ry3 += mulc
+# asm 1: add <mulc=int64#14,<ry3=int64#13
+# asm 2: add <mulc=%rbx,<ry3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<pp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r8
+adc %rdx,%r8
+
+# qhasm: mulx2 = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>mulx2=int64#9
+# asm 2: movq 80(<pp=%rsi),>mulx2=%r11
+movq 80(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<pp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? ry2 += mulrax
+# asm 1: add <mulrax=int64#7,<ry2=int64#12
+# asm 2: add <mulrax=%rax,<ry2=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<pp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#13
+# asm 2: add <mulrax=%rax,<ry3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? ry3 += mulc
+# asm 1: add <mulc=int64#14,<ry3=int64#13
+# asm 2: add <mulc=%rbx,<ry3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<pp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<pp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: mulx3 = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>mulx3=int64#9
+# asm 2: movq 88(<pp=%rsi),>mulx3=%r11
+movq 88(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<pp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#13
+# asm 2: add <mulrax=%rax,<ry3=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<pp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<pp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<pp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#14,<mulr6=int64#6
+# asm 2: add <mulc=%rbx,<mulr6=%r9
+add %rbx,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr4=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#4
+# asm 2: mov <mulrax=%rax,>mulr4=%rcx
+mov %rax,%rcx
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr5=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#5
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r8
+mov %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr6=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr7=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#8
+# asm 2: add <mulrax=%rax,<mulr7=%r10
+add %rax,%r10
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? ry0 += mulr4
+# asm 1: add <mulr4=int64#4,<ry0=int64#10
+# asm 2: add <mulr4=%rcx,<ry0=%r12
+add %rcx,%r12
+
+# qhasm: carry? ry1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#5,<ry1=int64#11
+# asm 2: adc <mulr5=%r8,<ry1=%r13
+adc %r8,%r13
+
+# qhasm: carry? ry2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#6,<ry2=int64#12
+# asm 2: adc <mulr6=%r9,<ry2=%r14
+adc %r9,%r14
+
+# qhasm: carry? ry3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#8,<ry3=int64#13
+# asm 2: adc <mulr7=%r10,<ry3=%r15
+adc %r10,%r15
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#4
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rcx
+imulq $38,%rax,%rcx
+
+# qhasm: carry? ry0 += mulr8
+# asm 1: add <mulr8=int64#4,<ry0=int64#10
+# asm 2: add <mulr8=%rcx,<ry0=%r12
+add %rcx,%r12
+
+# qhasm: carry? ry1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<ry1=int64#11
+# asm 2: adc <mulzero=%rdx,<ry1=%r13
+adc %rdx,%r13
+
+# qhasm: carry? ry2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<ry2=int64#12
+# asm 2: adc <mulzero=%rdx,<ry2=%r14
+adc %rdx,%r14
+
+# qhasm: carry? ry3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<ry3=int64#13
+# asm 2: adc <mulzero=%rdx,<ry3=%r15
+adc %rdx,%r15
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: ry0 += mulzero
+# asm 1: add <mulzero=int64#3,<ry0=int64#10
+# asm 2: add <mulzero=%rdx,<ry0=%r12
+add %rdx,%r12
+
+# qhasm: *(uint64 *)(rp + 32) = ry0
+# asm 1: movq <ry0=int64#10,32(<rp=int64#1)
+# asm 2: movq <ry0=%r12,32(<rp=%rdi)
+movq %r12,32(%rdi)
+
+# qhasm: *(uint64 *)(rp + 40) = ry1
+# asm 1: movq <ry1=int64#11,40(<rp=int64#1)
+# asm 2: movq <ry1=%r13,40(<rp=%rdi)
+movq %r13,40(%rdi)
+
+# qhasm: *(uint64 *)(rp + 48) = ry2
+# asm 1: movq <ry2=int64#12,48(<rp=int64#1)
+# asm 2: movq <ry2=%r14,48(<rp=%rdi)
+movq %r14,48(%rdi)
+
+# qhasm: *(uint64 *)(rp + 56) = ry3
+# asm 1: movq <ry3=int64#13,56(<rp=int64#1)
+# asm 2: movq <ry3=%r15,56(<rp=%rdi)
+movq %r15,56(%rdi)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#4
+# asm 2: mov $0,>mulr4=%rcx
+mov $0,%rcx
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#5
+# asm 2: mov $0,>mulr5=%r8
+mov $0,%r8
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulx0 = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>mulx0=int64#9
+# asm 2: movq 32(<pp=%rsi),>mulx0=%r11
+movq 32(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: rz0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rz0=int64#10
+# asm 2: mov <mulrax=%rax,>rz0=%r12
+mov %rax,%r12
+
+# qhasm: rz1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rz1=int64#11
+# asm 2: mov <mulrdx=%rdx,>rz1=%r13
+mov %rdx,%r13
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rz1 += mulrax
+# asm 1: add <mulrax=int64#7,<rz1=int64#11
+# asm 2: add <mulrax=%rax,<rz1=%r13
+add %rax,%r13
+
+# qhasm: rz2 = 0
+# asm 1: mov $0,>rz2=int64#12
+# asm 2: mov $0,>rz2=%r14
+mov $0,%r14
+
+# qhasm: rz2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rz2=int64#12
+# asm 2: adc <mulrdx=%rdx,<rz2=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rz2 += mulrax
+# asm 1: add <mulrax=int64#7,<rz2=int64#12
+# asm 2: add <mulrax=%rax,<rz2=%r14
+add %rax,%r14
+
+# qhasm: rz3 = 0
+# asm 1: mov $0,>rz3=int64#13
+# asm 2: mov $0,>rz3=%r15
+mov $0,%r15
+
+# qhasm: rz3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rz3=int64#13
+# asm 2: adc <mulrdx=%rdx,<rz3=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#13
+# asm 2: add <mulrax=%rax,<rz3=%r15
+add %rax,%r15
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx1 = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>mulx1=int64#9
+# asm 2: movq 40(<pp=%rsi),>mulx1=%r11
+movq 40(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rz1 += mulrax
+# asm 1: add <mulrax=int64#7,<rz1=int64#11
+# asm 2: add <mulrax=%rax,<rz1=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rz2 += mulrax
+# asm 1: add <mulrax=int64#7,<rz2=int64#12
+# asm 2: add <mulrax=%rax,<rz2=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rz2 += mulc
+# asm 1: add <mulc=int64#14,<rz2=int64#12
+# asm 2: add <mulc=%rbx,<rz2=%r14
+add %rbx,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#13
+# asm 2: add <mulrax=%rax,<rz3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rz3 += mulc
+# asm 1: add <mulc=int64#14,<rz3=int64#13
+# asm 2: add <mulc=%rbx,<rz3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r8
+adc %rdx,%r8
+
+# qhasm: mulx2 = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>mulx2=int64#9
+# asm 2: movq 48(<pp=%rsi),>mulx2=%r11
+movq 48(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? rz2 += mulrax
+# asm 1: add <mulrax=int64#7,<rz2=int64#12
+# asm 2: add <mulrax=%rax,<rz2=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#13
+# asm 2: add <mulrax=%rax,<rz3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rz3 += mulc
+# asm 1: add <mulc=int64#14,<rz3=int64#13
+# asm 2: add <mulc=%rbx,<rz3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: mulx3 = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>mulx3=int64#9
+# asm 2: movq 56(<pp=%rsi),>mulx3=%r11
+movq 56(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#13
+# asm 2: add <mulrax=%rax,<rz3=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#14,<mulr6=int64#6
+# asm 2: add <mulc=%rbx,<mulr6=%r9
+add %rbx,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr4=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#4
+# asm 2: mov <mulrax=%rax,>mulr4=%rcx
+mov %rax,%rcx
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr5=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#5
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r8
+mov %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr6=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr7=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#8
+# asm 2: add <mulrax=%rax,<mulr7=%r10
+add %rax,%r10
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rz0 += mulr4
+# asm 1: add <mulr4=int64#4,<rz0=int64#10
+# asm 2: add <mulr4=%rcx,<rz0=%r12
+add %rcx,%r12
+
+# qhasm: carry? rz1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#5,<rz1=int64#11
+# asm 2: adc <mulr5=%r8,<rz1=%r13
+adc %r8,%r13
+
+# qhasm: carry? rz2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#6,<rz2=int64#12
+# asm 2: adc <mulr6=%r9,<rz2=%r14
+adc %r9,%r14
+
+# qhasm: carry? rz3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#8,<rz3=int64#13
+# asm 2: adc <mulr7=%r10,<rz3=%r15
+adc %r10,%r15
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#4
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rcx
+imulq $38,%rax,%rcx
+
+# qhasm: carry? rz0 += mulr8
+# asm 1: add <mulr8=int64#4,<rz0=int64#10
+# asm 2: add <mulr8=%rcx,<rz0=%r12
+add %rcx,%r12
+
+# qhasm: carry? rz1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rz1=int64#11
+# asm 2: adc <mulzero=%rdx,<rz1=%r13
+adc %rdx,%r13
+
+# qhasm: carry? rz2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rz2=int64#12
+# asm 2: adc <mulzero=%rdx,<rz2=%r14
+adc %rdx,%r14
+
+# qhasm: carry? rz3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rz3=int64#13
+# asm 2: adc <mulzero=%rdx,<rz3=%r15
+adc %rdx,%r15
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: rz0 += mulzero
+# asm 1: add <mulzero=int64#3,<rz0=int64#10
+# asm 2: add <mulzero=%rdx,<rz0=%r12
+add %rdx,%r12
+
+# qhasm: *(uint64 *)(rp + 64) = rz0
+# asm 1: movq <rz0=int64#10,64(<rp=int64#1)
+# asm 2: movq <rz0=%r12,64(<rp=%rdi)
+movq %r12,64(%rdi)
+
+# qhasm: *(uint64 *)(rp + 72) = rz1
+# asm 1: movq <rz1=int64#11,72(<rp=int64#1)
+# asm 2: movq <rz1=%r13,72(<rp=%rdi)
+movq %r13,72(%rdi)
+
+# qhasm: *(uint64 *)(rp + 80) = rz2
+# asm 1: movq <rz2=int64#12,80(<rp=int64#1)
+# asm 2: movq <rz2=%r14,80(<rp=%rdi)
+movq %r14,80(%rdi)
+
+# qhasm: *(uint64 *)(rp + 88) = rz3
+# asm 1: movq <rz3=int64#13,88(<rp=int64#1)
+# asm 2: movq <rz3=%r15,88(<rp=%rdi)
+movq %r15,88(%rdi)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#4
+# asm 2: mov $0,>mulr4=%rcx
+mov $0,%rcx
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#5
+# asm 2: mov $0,>mulr5=%r8
+mov $0,%r8
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulx0 = *(uint64 *)(pp + 0)
+# asm 1: movq 0(<pp=int64#2),>mulx0=int64#9
+# asm 2: movq 0(<pp=%rsi),>mulx0=%r11
+movq 0(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 64)
+# asm 1: movq 64(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 64(<pp=%rsi),>mulrax=%rax
+movq 64(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: rt0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rt0=int64#10
+# asm 2: mov <mulrax=%rax,>rt0=%r12
+mov %rax,%r12
+
+# qhasm: rt1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rt1=int64#11
+# asm 2: mov <mulrdx=%rdx,>rt1=%r13
+mov %rdx,%r13
+
+# qhasm: mulrax = *(uint64 *)(pp + 72)
+# asm 1: movq 72(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 72(<pp=%rsi),>mulrax=%rax
+movq 72(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rt1 += mulrax
+# asm 1: add <mulrax=int64#7,<rt1=int64#11
+# asm 2: add <mulrax=%rax,<rt1=%r13
+add %rax,%r13
+
+# qhasm: rt2 = 0
+# asm 1: mov $0,>rt2=int64#12
+# asm 2: mov $0,>rt2=%r14
+mov $0,%r14
+
+# qhasm: rt2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rt2=int64#12
+# asm 2: adc <mulrdx=%rdx,<rt2=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 80(<pp=%rsi),>mulrax=%rax
+movq 80(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#12
+# asm 2: add <mulrax=%rax,<rt2=%r14
+add %rax,%r14
+
+# qhasm: rt3 = 0
+# asm 1: mov $0,>rt3=int64#13
+# asm 2: mov $0,>rt3=%r15
+mov $0,%r15
+
+# qhasm: rt3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rt3=int64#13
+# asm 2: adc <mulrdx=%rdx,<rt3=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 88(<pp=%rsi),>mulrax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#13
+# asm 2: add <mulrax=%rax,<rt3=%r15
+add %rax,%r15
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx1 = *(uint64 *)(pp + 8)
+# asm 1: movq 8(<pp=int64#2),>mulx1=int64#9
+# asm 2: movq 8(<pp=%rsi),>mulx1=%r11
+movq 8(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 64)
+# asm 1: movq 64(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 64(<pp=%rsi),>mulrax=%rax
+movq 64(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rt1 += mulrax
+# asm 1: add <mulrax=int64#7,<rt1=int64#11
+# asm 2: add <mulrax=%rax,<rt1=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 72)
+# asm 1: movq 72(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 72(<pp=%rsi),>mulrax=%rax
+movq 72(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#12
+# asm 2: add <mulrax=%rax,<rt2=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt2 += mulc
+# asm 1: add <mulc=int64#14,<rt2=int64#12
+# asm 2: add <mulc=%rbx,<rt2=%r14
+add %rbx,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 80(<pp=%rsi),>mulrax=%rax
+movq 80(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#13
+# asm 2: add <mulrax=%rax,<rt3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt3 += mulc
+# asm 1: add <mulc=int64#14,<rt3=int64#13
+# asm 2: add <mulc=%rbx,<rt3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 88(<pp=%rsi),>mulrax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r8
+adc %rdx,%r8
+
+# qhasm: mulx2 = *(uint64 *)(pp + 16)
+# asm 1: movq 16(<pp=int64#2),>mulx2=int64#9
+# asm 2: movq 16(<pp=%rsi),>mulx2=%r11
+movq 16(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 64)
+# asm 1: movq 64(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 64(<pp=%rsi),>mulrax=%rax
+movq 64(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#12
+# asm 2: add <mulrax=%rax,<rt2=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 72)
+# asm 1: movq 72(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 72(<pp=%rsi),>mulrax=%rax
+movq 72(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#13
+# asm 2: add <mulrax=%rax,<rt3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt3 += mulc
+# asm 1: add <mulc=int64#14,<rt3=int64#13
+# asm 2: add <mulc=%rbx,<rt3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 80(<pp=%rsi),>mulrax=%rax
+movq 80(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 88(<pp=%rsi),>mulrax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: mulx3 = *(uint64 *)(pp + 24)
+# asm 1: movq 24(<pp=int64#2),>mulx3=int64#9
+# asm 2: movq 24(<pp=%rsi),>mulx3=%r11
+movq 24(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 64)
+# asm 1: movq 64(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 64(<pp=%rsi),>mulrax=%rax
+movq 64(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#13
+# asm 2: add <mulrax=%rax,<rt3=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 72)
+# asm 1: movq 72(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 72(<pp=%rsi),>mulrax=%rax
+movq 72(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 80(<pp=%rsi),>mulrax=%rax
+movq 80(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 88(<pp=%rsi),>mulrax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#14,<mulr6=int64#6
+# asm 2: add <mulc=%rbx,<mulr6=%r9
+add %rbx,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr4=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#2
+# asm 2: mov <mulrax=%rax,>mulr4=%rsi
+mov %rax,%rsi
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr5=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#4
+# asm 2: mov <mulrdx=%rdx,>mulr5=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr6=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr7=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#6
+# asm 2: add <mulrax=%rax,<mulr7=%r9
+add %rax,%r9
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rt0 += mulr4
+# asm 1: add <mulr4=int64#2,<rt0=int64#10
+# asm 2: add <mulr4=%rsi,<rt0=%r12
+add %rsi,%r12
+
+# qhasm: carry? rt1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#4,<rt1=int64#11
+# asm 2: adc <mulr5=%rcx,<rt1=%r13
+adc %rcx,%r13
+
+# qhasm: carry? rt2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#5,<rt2=int64#12
+# asm 2: adc <mulr6=%r8,<rt2=%r14
+adc %r8,%r14
+
+# qhasm: carry? rt3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#6,<rt3=int64#13
+# asm 2: adc <mulr7=%r9,<rt3=%r15
+adc %r9,%r15
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#2
+# asm 2: mov $0,>mulzero=%rsi
+mov $0,%rsi
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulr8=int64#7
+# asm 2: adc <mulzero=%rsi,<mulr8=%rax
+adc %rsi,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#3
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? rt0 += mulr8
+# asm 1: add <mulr8=int64#3,<rt0=int64#10
+# asm 2: add <mulr8=%rdx,<rt0=%r12
+add %rdx,%r12
+
+# qhasm: carry? rt1 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt1=int64#11
+# asm 2: adc <mulzero=%rsi,<rt1=%r13
+adc %rsi,%r13
+
+# qhasm: carry? rt2 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt2=int64#12
+# asm 2: adc <mulzero=%rsi,<rt2=%r14
+adc %rsi,%r14
+
+# qhasm: carry? rt3 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt3=int64#13
+# asm 2: adc <mulzero=%rsi,<rt3=%r15
+adc %rsi,%r15
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulzero=int64#2
+# asm 2: adc <mulzero=%rsi,<mulzero=%rsi
+adc %rsi,%rsi
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#2,>mulzero=int64#2
+# asm 2: imulq $38,<mulzero=%rsi,>mulzero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: rt0 += mulzero
+# asm 1: add <mulzero=int64#2,<rt0=int64#10
+# asm 2: add <mulzero=%rsi,<rt0=%r12
+add %rsi,%r12
+
+# qhasm: *(uint64 *)(rp + 96) = rt0
+# asm 1: movq <rt0=int64#10,96(<rp=int64#1)
+# asm 2: movq <rt0=%r12,96(<rp=%rdi)
+movq %r12,96(%rdi)
+
+# qhasm: *(uint64 *)(rp + 104) = rt1
+# asm 1: movq <rt1=int64#11,104(<rp=int64#1)
+# asm 2: movq <rt1=%r13,104(<rp=%rdi)
+movq %r13,104(%rdi)
+
+# qhasm: *(uint64 *)(rp + 112) = rt2
+# asm 1: movq <rt2=int64#12,112(<rp=int64#1)
+# asm 2: movq <rt2=%r14,112(<rp=%rdi)
+movq %r14,112(%rdi)
+
+# qhasm: *(uint64 *)(rp + 120) = rt3
+# asm 1: movq <rt3=int64#13,120(<rp=int64#1)
+# asm 2: movq <rt3=%r15,120(<rp=%rdi)
+movq %r15,120(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/ge25519_pack.c b/ext/ed25519-amd64-asm/ge25519_pack.c
new file mode 100644
index 00000000..f289fe57
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_pack.c
@@ -0,0 +1,13 @@
+#include "fe25519.h"
+#include "sc25519.h"
+#include "ge25519.h"
+
+void ge25519_pack(unsigned char r[32], const ge25519_p3 *p)
+{
+ fe25519 tx, ty, zi;
+ fe25519_invert(&zi, &p->z);
+ fe25519_mul(&tx, &p->x, &zi);
+ fe25519_mul(&ty, &p->y, &zi);
+ fe25519_pack(r, &ty);
+ r[31] ^= fe25519_getparity(&tx) << 7;
+}
diff --git a/ext/ed25519-amd64-asm/ge25519_pnielsadd_p1p1.s b/ext/ed25519-amd64-asm/ge25519_pnielsadd_p1p1.s
new file mode 100644
index 00000000..93b7cc54
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_pnielsadd_p1p1.s
@@ -0,0 +1,3662 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 pp
+
+# qhasm: int64 qp
+
+# qhasm: input rp
+
+# qhasm: input pp
+
+# qhasm: input qp
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: int64 a0
+
+# qhasm: int64 a1
+
+# qhasm: int64 a2
+
+# qhasm: int64 a3
+
+# qhasm: stack64 a0_stack
+
+# qhasm: stack64 a1_stack
+
+# qhasm: stack64 a2_stack
+
+# qhasm: stack64 a3_stack
+
+# qhasm: int64 b0
+
+# qhasm: int64 b1
+
+# qhasm: int64 b2
+
+# qhasm: int64 b3
+
+# qhasm: stack64 b0_stack
+
+# qhasm: stack64 b1_stack
+
+# qhasm: stack64 b2_stack
+
+# qhasm: stack64 b3_stack
+
+# qhasm: int64 c0
+
+# qhasm: int64 c1
+
+# qhasm: int64 c2
+
+# qhasm: int64 c3
+
+# qhasm: stack64 c0_stack
+
+# qhasm: stack64 c1_stack
+
+# qhasm: stack64 c2_stack
+
+# qhasm: stack64 c3_stack
+
+# qhasm: int64 d0
+
+# qhasm: int64 d1
+
+# qhasm: int64 d2
+
+# qhasm: int64 d3
+
+# qhasm: stack64 d0_stack
+
+# qhasm: stack64 d1_stack
+
+# qhasm: stack64 d2_stack
+
+# qhasm: stack64 d3_stack
+
+# qhasm: int64 t10
+
+# qhasm: int64 t11
+
+# qhasm: int64 t12
+
+# qhasm: int64 t13
+
+# qhasm: stack64 t10_stack
+
+# qhasm: stack64 t11_stack
+
+# qhasm: stack64 t12_stack
+
+# qhasm: stack64 t13_stack
+
+# qhasm: int64 t20
+
+# qhasm: int64 t21
+
+# qhasm: int64 t22
+
+# qhasm: int64 t23
+
+# qhasm: stack64 t20_stack
+
+# qhasm: stack64 t21_stack
+
+# qhasm: stack64 t22_stack
+
+# qhasm: stack64 t23_stack
+
+# qhasm: int64 rx0
+
+# qhasm: int64 rx1
+
+# qhasm: int64 rx2
+
+# qhasm: int64 rx3
+
+# qhasm: int64 ry0
+
+# qhasm: int64 ry1
+
+# qhasm: int64 ry2
+
+# qhasm: int64 ry3
+
+# qhasm: int64 rz0
+
+# qhasm: int64 rz1
+
+# qhasm: int64 rz2
+
+# qhasm: int64 rz3
+
+# qhasm: int64 rt0
+
+# qhasm: int64 rt1
+
+# qhasm: int64 rt2
+
+# qhasm: int64 rt3
+
+# qhasm: int64 x0
+
+# qhasm: int64 x1
+
+# qhasm: int64 x2
+
+# qhasm: int64 x3
+
+# qhasm: int64 mulr4
+
+# qhasm: int64 mulr5
+
+# qhasm: int64 mulr6
+
+# qhasm: int64 mulr7
+
+# qhasm: int64 mulr8
+
+# qhasm: int64 mulrax
+
+# qhasm: int64 mulrdx
+
+# qhasm: int64 mulx0
+
+# qhasm: int64 mulx1
+
+# qhasm: int64 mulx2
+
+# qhasm: int64 mulx3
+
+# qhasm: int64 mulc
+
+# qhasm: int64 mulzero
+
+# qhasm: int64 muli38
+
+# qhasm: int64 addt0
+
+# qhasm: int64 addt1
+
+# qhasm: int64 subt0
+
+# qhasm: int64 subt1
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_ge25519_pnielsadd_p1p1
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_ge25519_pnielsadd_p1p1
+.globl crypto_sign_ed25519_amd64_64_ge25519_pnielsadd_p1p1
+_crypto_sign_ed25519_amd64_64_ge25519_pnielsadd_p1p1:
+crypto_sign_ed25519_amd64_64_ge25519_pnielsadd_p1p1:
+mov %rsp,%r11
+and $31,%r11
+add $128,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: qp = qp
+# asm 1: mov <qp=int64#3,>qp=int64#4
+# asm 2: mov <qp=%rdx,>qp=%rcx
+mov %rdx,%rcx
+
+# qhasm: a0 = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>a0=int64#3
+# asm 2: movq 32(<pp=%rsi),>a0=%rdx
+movq 32(%rsi),%rdx
+
+# qhasm: a1 = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>a1=int64#5
+# asm 2: movq 40(<pp=%rsi),>a1=%r8
+movq 40(%rsi),%r8
+
+# qhasm: a2 = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>a2=int64#6
+# asm 2: movq 48(<pp=%rsi),>a2=%r9
+movq 48(%rsi),%r9
+
+# qhasm: a3 = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>a3=int64#7
+# asm 2: movq 56(<pp=%rsi),>a3=%rax
+movq 56(%rsi),%rax
+
+# qhasm: b0 = a0
+# asm 1: mov <a0=int64#3,>b0=int64#8
+# asm 2: mov <a0=%rdx,>b0=%r10
+mov %rdx,%r10
+
+# qhasm: b1 = a1
+# asm 1: mov <a1=int64#5,>b1=int64#9
+# asm 2: mov <a1=%r8,>b1=%r11
+mov %r8,%r11
+
+# qhasm: b2 = a2
+# asm 1: mov <a2=int64#6,>b2=int64#10
+# asm 2: mov <a2=%r9,>b2=%r12
+mov %r9,%r12
+
+# qhasm: b3 = a3
+# asm 1: mov <a3=int64#7,>b3=int64#11
+# asm 2: mov <a3=%rax,>b3=%r13
+mov %rax,%r13
+
+# qhasm: carry? a0 -= *(uint64 *)(pp + 0)
+# asm 1: subq 0(<pp=int64#2),<a0=int64#3
+# asm 2: subq 0(<pp=%rsi),<a0=%rdx
+subq 0(%rsi),%rdx
+
+# qhasm: carry? a1 -= *(uint64 *)(pp + 8) - carry
+# asm 1: sbbq 8(<pp=int64#2),<a1=int64#5
+# asm 2: sbbq 8(<pp=%rsi),<a1=%r8
+sbbq 8(%rsi),%r8
+
+# qhasm: carry? a2 -= *(uint64 *)(pp + 16) - carry
+# asm 1: sbbq 16(<pp=int64#2),<a2=int64#6
+# asm 2: sbbq 16(<pp=%rsi),<a2=%r9
+sbbq 16(%rsi),%r9
+
+# qhasm: carry? a3 -= *(uint64 *)(pp + 24) - carry
+# asm 1: sbbq 24(<pp=int64#2),<a3=int64#7
+# asm 2: sbbq 24(<pp=%rsi),<a3=%rax
+sbbq 24(%rsi),%rax
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#12
+# asm 2: mov $0,>subt0=%r14
+mov $0,%r14
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#13
+# asm 2: mov $38,>subt1=%r15
+mov $38,%r15
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#12,<subt1=int64#13
+# asm 2: cmovae <subt0=%r14,<subt1=%r15
+cmovae %r14,%r15
+
+# qhasm: carry? a0 -= subt1
+# asm 1: sub <subt1=int64#13,<a0=int64#3
+# asm 2: sub <subt1=%r15,<a0=%rdx
+sub %r15,%rdx
+
+# qhasm: carry? a1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<a1=int64#5
+# asm 2: sbb <subt0=%r14,<a1=%r8
+sbb %r14,%r8
+
+# qhasm: carry? a2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<a2=int64#6
+# asm 2: sbb <subt0=%r14,<a2=%r9
+sbb %r14,%r9
+
+# qhasm: carry? a3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<a3=int64#7
+# asm 2: sbb <subt0=%r14,<a3=%rax
+sbb %r14,%rax
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#13,<subt0=int64#12
+# asm 2: cmovc <subt1=%r15,<subt0=%r14
+cmovc %r15,%r14
+
+# qhasm: a0 -= subt0
+# asm 1: sub <subt0=int64#12,<a0=int64#3
+# asm 2: sub <subt0=%r14,<a0=%rdx
+sub %r14,%rdx
+
+# qhasm: carry? b0 += *(uint64 *)(pp + 0)
+# asm 1: addq 0(<pp=int64#2),<b0=int64#8
+# asm 2: addq 0(<pp=%rsi),<b0=%r10
+addq 0(%rsi),%r10
+
+# qhasm: carry? b1 += *(uint64 *)(pp + 8) + carry
+# asm 1: adcq 8(<pp=int64#2),<b1=int64#9
+# asm 2: adcq 8(<pp=%rsi),<b1=%r11
+adcq 8(%rsi),%r11
+
+# qhasm: carry? b2 += *(uint64 *)(pp + 16) + carry
+# asm 1: adcq 16(<pp=int64#2),<b2=int64#10
+# asm 2: adcq 16(<pp=%rsi),<b2=%r12
+adcq 16(%rsi),%r12
+
+# qhasm: carry? b3 += *(uint64 *)(pp + 24) + carry
+# asm 1: adcq 24(<pp=int64#2),<b3=int64#11
+# asm 2: adcq 24(<pp=%rsi),<b3=%r13
+adcq 24(%rsi),%r13
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#12
+# asm 2: mov $0,>addt0=%r14
+mov $0,%r14
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#13
+# asm 2: mov $38,>addt1=%r15
+mov $38,%r15
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#12,<addt1=int64#13
+# asm 2: cmovae <addt0=%r14,<addt1=%r15
+cmovae %r14,%r15
+
+# qhasm: carry? b0 += addt1
+# asm 1: add <addt1=int64#13,<b0=int64#8
+# asm 2: add <addt1=%r15,<b0=%r10
+add %r15,%r10
+
+# qhasm: carry? b1 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<b1=int64#9
+# asm 2: adc <addt0=%r14,<b1=%r11
+adc %r14,%r11
+
+# qhasm: carry? b2 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<b2=int64#10
+# asm 2: adc <addt0=%r14,<b2=%r12
+adc %r14,%r12
+
+# qhasm: carry? b3 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<b3=int64#11
+# asm 2: adc <addt0=%r14,<b3=%r13
+adc %r14,%r13
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#13,<addt0=int64#12
+# asm 2: cmovc <addt1=%r15,<addt0=%r14
+cmovc %r15,%r14
+
+# qhasm: b0 += addt0
+# asm 1: add <addt0=int64#12,<b0=int64#8
+# asm 2: add <addt0=%r14,<b0=%r10
+add %r14,%r10
+
+# qhasm: a0_stack = a0
+# asm 1: movq <a0=int64#3,>a0_stack=stack64#8
+# asm 2: movq <a0=%rdx,>a0_stack=56(%rsp)
+movq %rdx,56(%rsp)
+
+# qhasm: a1_stack = a1
+# asm 1: movq <a1=int64#5,>a1_stack=stack64#9
+# asm 2: movq <a1=%r8,>a1_stack=64(%rsp)
+movq %r8,64(%rsp)
+
+# qhasm: a2_stack = a2
+# asm 1: movq <a2=int64#6,>a2_stack=stack64#10
+# asm 2: movq <a2=%r9,>a2_stack=72(%rsp)
+movq %r9,72(%rsp)
+
+# qhasm: a3_stack = a3
+# asm 1: movq <a3=int64#7,>a3_stack=stack64#11
+# asm 2: movq <a3=%rax,>a3_stack=80(%rsp)
+movq %rax,80(%rsp)
+
+# qhasm: b0_stack = b0
+# asm 1: movq <b0=int64#8,>b0_stack=stack64#12
+# asm 2: movq <b0=%r10,>b0_stack=88(%rsp)
+movq %r10,88(%rsp)
+
+# qhasm: b1_stack = b1
+# asm 1: movq <b1=int64#9,>b1_stack=stack64#13
+# asm 2: movq <b1=%r11,>b1_stack=96(%rsp)
+movq %r11,96(%rsp)
+
+# qhasm: b2_stack = b2
+# asm 1: movq <b2=int64#10,>b2_stack=stack64#14
+# asm 2: movq <b2=%r12,>b2_stack=104(%rsp)
+movq %r12,104(%rsp)
+
+# qhasm: b3_stack = b3
+# asm 1: movq <b3=int64#11,>b3_stack=stack64#15
+# asm 2: movq <b3=%r13,>b3_stack=112(%rsp)
+movq %r13,112(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = a0_stack
+# asm 1: movq <a0_stack=stack64#8,>mulx0=int64#10
+# asm 2: movq <a0_stack=56(%rsp),>mulx0=%r12
+movq 56(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: a0 = mulrax
+# asm 1: mov <mulrax=int64#7,>a0=int64#11
+# asm 2: mov <mulrax=%rax,>a0=%r13
+mov %rax,%r13
+
+# qhasm: a1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>a1=int64#12
+# asm 2: mov <mulrdx=%rdx,>a1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? a1 += mulrax
+# asm 1: add <mulrax=int64#7,<a1=int64#12
+# asm 2: add <mulrax=%rax,<a1=%r14
+add %rax,%r14
+
+# qhasm: a2 = 0
+# asm 1: mov $0,>a2=int64#13
+# asm 2: mov $0,>a2=%r15
+mov $0,%r15
+
+# qhasm: a2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<a2=int64#13
+# asm 2: adc <mulrdx=%rdx,<a2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#13
+# asm 2: add <mulrax=%rax,<a2=%r15
+add %rax,%r15
+
+# qhasm: a3 = 0
+# asm 1: mov $0,>a3=int64#14
+# asm 2: mov $0,>a3=%rbx
+mov $0,%rbx
+
+# qhasm: a3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<a3=int64#14
+# asm 2: adc <mulrdx=%rdx,<a3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = a1_stack
+# asm 1: movq <a1_stack=stack64#9,>mulx1=int64#10
+# asm 2: movq <a1_stack=64(%rsp),>mulx1=%r12
+movq 64(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? a1 += mulrax
+# asm 1: add <mulrax=int64#7,<a1=int64#12
+# asm 2: add <mulrax=%rax,<a1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#13
+# asm 2: add <mulrax=%rax,<a2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a2 += mulc
+# asm 1: add <mulc=int64#15,<a2=int64#13
+# asm 2: add <mulc=%rbp,<a2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a3 += mulc
+# asm 1: add <mulc=int64#15,<a3=int64#14
+# asm 2: add <mulc=%rbp,<a3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = a2_stack
+# asm 1: movq <a2_stack=stack64#10,>mulx2=int64#10
+# asm 2: movq <a2_stack=72(%rsp),>mulx2=%r12
+movq 72(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#13
+# asm 2: add <mulrax=%rax,<a2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a3 += mulc
+# asm 1: add <mulc=int64#15,<a3=int64#14
+# asm 2: add <mulc=%rbp,<a3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = a3_stack
+# asm 1: movq <a3_stack=stack64#11,>mulx3=int64#10
+# asm 2: movq <a3_stack=80(%rsp),>mulx3=%r12
+movq 80(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#15,<mulr6=int64#8
+# asm 2: add <mulc=%rbp,<mulr6=%r10
+add %rbp,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#5
+# asm 2: mov <mulrax=%rax,>mulr4=%r8
+mov %rax,%r8
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#6
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r9
+mov %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#9
+# asm 2: add <mulrax=%rax,<mulr7=%r11
+add %rax,%r11
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? a0 += mulr4
+# asm 1: add <mulr4=int64#5,<a0=int64#11
+# asm 2: add <mulr4=%r8,<a0=%r13
+add %r8,%r13
+
+# qhasm: carry? a1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#6,<a1=int64#12
+# asm 2: adc <mulr5=%r9,<a1=%r14
+adc %r9,%r14
+
+# qhasm: carry? a2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#8,<a2=int64#13
+# asm 2: adc <mulr6=%r10,<a2=%r15
+adc %r10,%r15
+
+# qhasm: carry? a3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#9,<a3=int64#14
+# asm 2: adc <mulr7=%r11,<a3=%rbx
+adc %r11,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#5
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%r8
+imulq $38,%rax,%r8
+
+# qhasm: carry? a0 += mulr8
+# asm 1: add <mulr8=int64#5,<a0=int64#11
+# asm 2: add <mulr8=%r8,<a0=%r13
+add %r8,%r13
+
+# qhasm: carry? a1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a1=int64#12
+# asm 2: adc <mulzero=%rdx,<a1=%r14
+adc %rdx,%r14
+
+# qhasm: carry? a2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a2=int64#13
+# asm 2: adc <mulzero=%rdx,<a2=%r15
+adc %rdx,%r15
+
+# qhasm: carry? a3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a3=int64#14
+# asm 2: adc <mulzero=%rdx,<a3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: a0 += mulzero
+# asm 1: add <mulzero=int64#3,<a0=int64#11
+# asm 2: add <mulzero=%rdx,<a0=%r13
+add %rdx,%r13
+
+# qhasm: a0_stack = a0
+# asm 1: movq <a0=int64#11,>a0_stack=stack64#8
+# asm 2: movq <a0=%r13,>a0_stack=56(%rsp)
+movq %r13,56(%rsp)
+
+# qhasm: a1_stack = a1
+# asm 1: movq <a1=int64#12,>a1_stack=stack64#9
+# asm 2: movq <a1=%r14,>a1_stack=64(%rsp)
+movq %r14,64(%rsp)
+
+# qhasm: a2_stack = a2
+# asm 1: movq <a2=int64#13,>a2_stack=stack64#10
+# asm 2: movq <a2=%r15,>a2_stack=72(%rsp)
+movq %r15,72(%rsp)
+
+# qhasm: a3_stack = a3
+# asm 1: movq <a3=int64#14,>a3_stack=stack64#11
+# asm 2: movq <a3=%rbx,>a3_stack=80(%rsp)
+movq %rbx,80(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = b0_stack
+# asm 1: movq <b0_stack=stack64#12,>mulx0=int64#10
+# asm 2: movq <b0_stack=88(%rsp),>mulx0=%r12
+movq 88(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rcx),>mulrax=%rax
+movq 32(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: rx0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rx0=int64#11
+# asm 2: mov <mulrax=%rax,>rx0=%r13
+mov %rax,%r13
+
+# qhasm: rx1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rx1=int64#12
+# asm 2: mov <mulrdx=%rdx,>rx1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rcx),>mulrax=%rax
+movq 40(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rx1 += mulrax
+# asm 1: add <mulrax=int64#7,<rx1=int64#12
+# asm 2: add <mulrax=%rax,<rx1=%r14
+add %rax,%r14
+
+# qhasm: rx2 = 0
+# asm 1: mov $0,>rx2=int64#13
+# asm 2: mov $0,>rx2=%r15
+mov $0,%r15
+
+# qhasm: rx2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rx2=int64#13
+# asm 2: adc <mulrdx=%rdx,<rx2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rcx),>mulrax=%rax
+movq 48(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#13
+# asm 2: add <mulrax=%rax,<rx2=%r15
+add %rax,%r15
+
+# qhasm: rx3 = 0
+# asm 1: mov $0,>rx3=int64#14
+# asm 2: mov $0,>rx3=%rbx
+mov $0,%rbx
+
+# qhasm: rx3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rx3=int64#14
+# asm 2: adc <mulrdx=%rdx,<rx3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rcx),>mulrax=%rax
+movq 56(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#14
+# asm 2: add <mulrax=%rax,<rx3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = b1_stack
+# asm 1: movq <b1_stack=stack64#13,>mulx1=int64#10
+# asm 2: movq <b1_stack=96(%rsp),>mulx1=%r12
+movq 96(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rcx),>mulrax=%rax
+movq 32(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rx1 += mulrax
+# asm 1: add <mulrax=int64#7,<rx1=int64#12
+# asm 2: add <mulrax=%rax,<rx1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rcx),>mulrax=%rax
+movq 40(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#13
+# asm 2: add <mulrax=%rax,<rx2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx2 += mulc
+# asm 1: add <mulc=int64#15,<rx2=int64#13
+# asm 2: add <mulc=%rbp,<rx2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rcx),>mulrax=%rax
+movq 48(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#14
+# asm 2: add <mulrax=%rax,<rx3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx3 += mulc
+# asm 1: add <mulc=int64#15,<rx3=int64#14
+# asm 2: add <mulc=%rbp,<rx3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rcx),>mulrax=%rax
+movq 56(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = b2_stack
+# asm 1: movq <b2_stack=stack64#14,>mulx2=int64#10
+# asm 2: movq <b2_stack=104(%rsp),>mulx2=%r12
+movq 104(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rcx),>mulrax=%rax
+movq 32(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#13
+# asm 2: add <mulrax=%rax,<rx2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rcx),>mulrax=%rax
+movq 40(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#14
+# asm 2: add <mulrax=%rax,<rx3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx3 += mulc
+# asm 1: add <mulc=int64#15,<rx3=int64#14
+# asm 2: add <mulc=%rbp,<rx3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rcx),>mulrax=%rax
+movq 48(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rcx),>mulrax=%rax
+movq 56(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = b3_stack
+# asm 1: movq <b3_stack=stack64#15,>mulx3=int64#10
+# asm 2: movq <b3_stack=112(%rsp),>mulx3=%r12
+movq 112(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rcx),>mulrax=%rax
+movq 32(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#14
+# asm 2: add <mulrax=%rax,<rx3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rcx),>mulrax=%rax
+movq 40(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rcx),>mulrax=%rax
+movq 48(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rcx),>mulrax=%rax
+movq 56(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#15,<mulr6=int64#8
+# asm 2: add <mulc=%rbp,<mulr6=%r10
+add %rbp,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#5
+# asm 2: mov <mulrax=%rax,>mulr4=%r8
+mov %rax,%r8
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#6
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r9
+mov %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#9
+# asm 2: add <mulrax=%rax,<mulr7=%r11
+add %rax,%r11
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rx0 += mulr4
+# asm 1: add <mulr4=int64#5,<rx0=int64#11
+# asm 2: add <mulr4=%r8,<rx0=%r13
+add %r8,%r13
+
+# qhasm: carry? rx1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#6,<rx1=int64#12
+# asm 2: adc <mulr5=%r9,<rx1=%r14
+adc %r9,%r14
+
+# qhasm: carry? rx2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#8,<rx2=int64#13
+# asm 2: adc <mulr6=%r10,<rx2=%r15
+adc %r10,%r15
+
+# qhasm: carry? rx3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#9,<rx3=int64#14
+# asm 2: adc <mulr7=%r11,<rx3=%rbx
+adc %r11,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#5
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%r8
+imulq $38,%rax,%r8
+
+# qhasm: carry? rx0 += mulr8
+# asm 1: add <mulr8=int64#5,<rx0=int64#11
+# asm 2: add <mulr8=%r8,<rx0=%r13
+add %r8,%r13
+
+# qhasm: carry? rx1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx1=int64#12
+# asm 2: adc <mulzero=%rdx,<rx1=%r14
+adc %rdx,%r14
+
+# qhasm: carry? rx2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx2=int64#13
+# asm 2: adc <mulzero=%rdx,<rx2=%r15
+adc %rdx,%r15
+
+# qhasm: carry? rx3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx3=int64#14
+# asm 2: adc <mulzero=%rdx,<rx3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: rx0 += mulzero
+# asm 1: add <mulzero=int64#3,<rx0=int64#11
+# asm 2: add <mulzero=%rdx,<rx0=%r13
+add %rdx,%r13
+
+# qhasm: ry0 = rx0
+# asm 1: mov <rx0=int64#11,>ry0=int64#3
+# asm 2: mov <rx0=%r13,>ry0=%rdx
+mov %r13,%rdx
+
+# qhasm: ry1 = rx1
+# asm 1: mov <rx1=int64#12,>ry1=int64#5
+# asm 2: mov <rx1=%r14,>ry1=%r8
+mov %r14,%r8
+
+# qhasm: ry2 = rx2
+# asm 1: mov <rx2=int64#13,>ry2=int64#6
+# asm 2: mov <rx2=%r15,>ry2=%r9
+mov %r15,%r9
+
+# qhasm: ry3 = rx3
+# asm 1: mov <rx3=int64#14,>ry3=int64#7
+# asm 2: mov <rx3=%rbx,>ry3=%rax
+mov %rbx,%rax
+
+# qhasm: carry? ry0 += a0_stack
+# asm 1: addq <a0_stack=stack64#8,<ry0=int64#3
+# asm 2: addq <a0_stack=56(%rsp),<ry0=%rdx
+addq 56(%rsp),%rdx
+
+# qhasm: carry? ry1 += a1_stack + carry
+# asm 1: adcq <a1_stack=stack64#9,<ry1=int64#5
+# asm 2: adcq <a1_stack=64(%rsp),<ry1=%r8
+adcq 64(%rsp),%r8
+
+# qhasm: carry? ry2 += a2_stack + carry
+# asm 1: adcq <a2_stack=stack64#10,<ry2=int64#6
+# asm 2: adcq <a2_stack=72(%rsp),<ry2=%r9
+adcq 72(%rsp),%r9
+
+# qhasm: carry? ry3 += a3_stack + carry
+# asm 1: adcq <a3_stack=stack64#11,<ry3=int64#7
+# asm 2: adcq <a3_stack=80(%rsp),<ry3=%rax
+adcq 80(%rsp),%rax
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#8
+# asm 2: mov $0,>addt0=%r10
+mov $0,%r10
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#9
+# asm 2: mov $38,>addt1=%r11
+mov $38,%r11
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#8,<addt1=int64#9
+# asm 2: cmovae <addt0=%r10,<addt1=%r11
+cmovae %r10,%r11
+
+# qhasm: carry? ry0 += addt1
+# asm 1: add <addt1=int64#9,<ry0=int64#3
+# asm 2: add <addt1=%r11,<ry0=%rdx
+add %r11,%rdx
+
+# qhasm: carry? ry1 += addt0 + carry
+# asm 1: adc <addt0=int64#8,<ry1=int64#5
+# asm 2: adc <addt0=%r10,<ry1=%r8
+adc %r10,%r8
+
+# qhasm: carry? ry2 += addt0 + carry
+# asm 1: adc <addt0=int64#8,<ry2=int64#6
+# asm 2: adc <addt0=%r10,<ry2=%r9
+adc %r10,%r9
+
+# qhasm: carry? ry3 += addt0 + carry
+# asm 1: adc <addt0=int64#8,<ry3=int64#7
+# asm 2: adc <addt0=%r10,<ry3=%rax
+adc %r10,%rax
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#9,<addt0=int64#8
+# asm 2: cmovc <addt1=%r11,<addt0=%r10
+cmovc %r11,%r10
+
+# qhasm: ry0 += addt0
+# asm 1: add <addt0=int64#8,<ry0=int64#3
+# asm 2: add <addt0=%r10,<ry0=%rdx
+add %r10,%rdx
+
+# qhasm: carry? rx0 -= a0_stack
+# asm 1: subq <a0_stack=stack64#8,<rx0=int64#11
+# asm 2: subq <a0_stack=56(%rsp),<rx0=%r13
+subq 56(%rsp),%r13
+
+# qhasm: carry? rx1 -= a1_stack - carry
+# asm 1: sbbq <a1_stack=stack64#9,<rx1=int64#12
+# asm 2: sbbq <a1_stack=64(%rsp),<rx1=%r14
+sbbq 64(%rsp),%r14
+
+# qhasm: carry? rx2 -= a2_stack - carry
+# asm 1: sbbq <a2_stack=stack64#10,<rx2=int64#13
+# asm 2: sbbq <a2_stack=72(%rsp),<rx2=%r15
+sbbq 72(%rsp),%r15
+
+# qhasm: carry? rx3 -= a3_stack - carry
+# asm 1: sbbq <a3_stack=stack64#11,<rx3=int64#14
+# asm 2: sbbq <a3_stack=80(%rsp),<rx3=%rbx
+sbbq 80(%rsp),%rbx
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#8
+# asm 2: mov $0,>subt0=%r10
+mov $0,%r10
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#9
+# asm 2: mov $38,>subt1=%r11
+mov $38,%r11
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#8,<subt1=int64#9
+# asm 2: cmovae <subt0=%r10,<subt1=%r11
+cmovae %r10,%r11
+
+# qhasm: carry? rx0 -= subt1
+# asm 1: sub <subt1=int64#9,<rx0=int64#11
+# asm 2: sub <subt1=%r11,<rx0=%r13
+sub %r11,%r13
+
+# qhasm: carry? rx1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#8,<rx1=int64#12
+# asm 2: sbb <subt0=%r10,<rx1=%r14
+sbb %r10,%r14
+
+# qhasm: carry? rx2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#8,<rx2=int64#13
+# asm 2: sbb <subt0=%r10,<rx2=%r15
+sbb %r10,%r15
+
+# qhasm: carry? rx3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#8,<rx3=int64#14
+# asm 2: sbb <subt0=%r10,<rx3=%rbx
+sbb %r10,%rbx
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#9,<subt0=int64#8
+# asm 2: cmovc <subt1=%r11,<subt0=%r10
+cmovc %r11,%r10
+
+# qhasm: rx0 -= subt0
+# asm 1: sub <subt0=int64#8,<rx0=int64#11
+# asm 2: sub <subt0=%r10,<rx0=%r13
+sub %r10,%r13
+
+# qhasm: *(uint64 *) (rp + 0) = rx0
+# asm 1: movq <rx0=int64#11,0(<rp=int64#1)
+# asm 2: movq <rx0=%r13,0(<rp=%rdi)
+movq %r13,0(%rdi)
+
+# qhasm: *(uint64 *) (rp + 8) = rx1
+# asm 1: movq <rx1=int64#12,8(<rp=int64#1)
+# asm 2: movq <rx1=%r14,8(<rp=%rdi)
+movq %r14,8(%rdi)
+
+# qhasm: *(uint64 *) (rp + 16) = rx2
+# asm 1: movq <rx2=int64#13,16(<rp=int64#1)
+# asm 2: movq <rx2=%r15,16(<rp=%rdi)
+movq %r15,16(%rdi)
+
+# qhasm: *(uint64 *) (rp + 24) = rx3
+# asm 1: movq <rx3=int64#14,24(<rp=int64#1)
+# asm 2: movq <rx3=%rbx,24(<rp=%rdi)
+movq %rbx,24(%rdi)
+
+# qhasm: *(uint64 *) (rp + 64) = ry0
+# asm 1: movq <ry0=int64#3,64(<rp=int64#1)
+# asm 2: movq <ry0=%rdx,64(<rp=%rdi)
+movq %rdx,64(%rdi)
+
+# qhasm: *(uint64 *) (rp + 72) = ry1
+# asm 1: movq <ry1=int64#5,72(<rp=int64#1)
+# asm 2: movq <ry1=%r8,72(<rp=%rdi)
+movq %r8,72(%rdi)
+
+# qhasm: *(uint64 *) (rp + 80) = ry2
+# asm 1: movq <ry2=int64#6,80(<rp=int64#1)
+# asm 2: movq <ry2=%r9,80(<rp=%rdi)
+movq %r9,80(%rdi)
+
+# qhasm: *(uint64 *) (rp + 88) = ry3
+# asm 1: movq <ry3=int64#7,88(<rp=int64#1)
+# asm 2: movq <ry3=%rax,88(<rp=%rdi)
+movq %rax,88(%rdi)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulx0=int64#10
+# asm 2: movq 96(<pp=%rsi),>mulx0=%r12
+movq 96(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 96)
+# asm 1: movq 96(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 96(<qp=%rcx),>mulrax=%rax
+movq 96(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: c0 = mulrax
+# asm 1: mov <mulrax=int64#7,>c0=int64#11
+# asm 2: mov <mulrax=%rax,>c0=%r13
+mov %rax,%r13
+
+# qhasm: c1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>c1=int64#12
+# asm 2: mov <mulrdx=%rdx,>c1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 104)
+# asm 1: movq 104(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 104(<qp=%rcx),>mulrax=%rax
+movq 104(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c1 += mulrax
+# asm 1: add <mulrax=int64#7,<c1=int64#12
+# asm 2: add <mulrax=%rax,<c1=%r14
+add %rax,%r14
+
+# qhasm: c2 = 0
+# asm 1: mov $0,>c2=int64#13
+# asm 2: mov $0,>c2=%r15
+mov $0,%r15
+
+# qhasm: c2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<c2=int64#13
+# asm 2: adc <mulrdx=%rdx,<c2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 112)
+# asm 1: movq 112(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 112(<qp=%rcx),>mulrax=%rax
+movq 112(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: c3 = 0
+# asm 1: mov $0,>c3=int64#14
+# asm 2: mov $0,>c3=%rbx
+mov $0,%rbx
+
+# qhasm: c3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<c3=int64#14
+# asm 2: adc <mulrdx=%rdx,<c3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 120)
+# asm 1: movq 120(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 120(<qp=%rcx),>mulrax=%rax
+movq 120(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulx1=int64#10
+# asm 2: movq 104(<pp=%rsi),>mulx1=%r12
+movq 104(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 96)
+# asm 1: movq 96(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 96(<qp=%rcx),>mulrax=%rax
+movq 96(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c1 += mulrax
+# asm 1: add <mulrax=int64#7,<c1=int64#12
+# asm 2: add <mulrax=%rax,<c1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 104)
+# asm 1: movq 104(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 104(<qp=%rcx),>mulrax=%rax
+movq 104(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c2 += mulc
+# asm 1: add <mulc=int64#15,<c2=int64#13
+# asm 2: add <mulc=%rbp,<c2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 112)
+# asm 1: movq 112(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 112(<qp=%rcx),>mulrax=%rax
+movq 112(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c3 += mulc
+# asm 1: add <mulc=int64#15,<c3=int64#14
+# asm 2: add <mulc=%rbp,<c3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 120)
+# asm 1: movq 120(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 120(<qp=%rcx),>mulrax=%rax
+movq 120(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulx2=int64#10
+# asm 2: movq 112(<pp=%rsi),>mulx2=%r12
+movq 112(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 96)
+# asm 1: movq 96(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 96(<qp=%rcx),>mulrax=%rax
+movq 96(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 104)
+# asm 1: movq 104(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 104(<qp=%rcx),>mulrax=%rax
+movq 104(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c3 += mulc
+# asm 1: add <mulc=int64#15,<c3=int64#14
+# asm 2: add <mulc=%rbp,<c3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 112)
+# asm 1: movq 112(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 112(<qp=%rcx),>mulrax=%rax
+movq 112(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 120)
+# asm 1: movq 120(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 120(<qp=%rcx),>mulrax=%rax
+movq 120(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulx3=int64#10
+# asm 2: movq 120(<pp=%rsi),>mulx3=%r12
+movq 120(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 96)
+# asm 1: movq 96(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 96(<qp=%rcx),>mulrax=%rax
+movq 96(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 104)
+# asm 1: movq 104(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 104(<qp=%rcx),>mulrax=%rax
+movq 104(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 112)
+# asm 1: movq 112(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 112(<qp=%rcx),>mulrax=%rax
+movq 112(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 120)
+# asm 1: movq 120(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 120(<qp=%rcx),>mulrax=%rax
+movq 120(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#15,<mulr6=int64#8
+# asm 2: add <mulc=%rbp,<mulr6=%r10
+add %rbp,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#5
+# asm 2: mov <mulrax=%rax,>mulr4=%r8
+mov %rax,%r8
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#6
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r9
+mov %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#9
+# asm 2: add <mulrax=%rax,<mulr7=%r11
+add %rax,%r11
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? c0 += mulr4
+# asm 1: add <mulr4=int64#5,<c0=int64#11
+# asm 2: add <mulr4=%r8,<c0=%r13
+add %r8,%r13
+
+# qhasm: carry? c1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#6,<c1=int64#12
+# asm 2: adc <mulr5=%r9,<c1=%r14
+adc %r9,%r14
+
+# qhasm: carry? c2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#8,<c2=int64#13
+# asm 2: adc <mulr6=%r10,<c2=%r15
+adc %r10,%r15
+
+# qhasm: carry? c3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#9,<c3=int64#14
+# asm 2: adc <mulr7=%r11,<c3=%rbx
+adc %r11,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#5
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%r8
+imulq $38,%rax,%r8
+
+# qhasm: carry? c0 += mulr8
+# asm 1: add <mulr8=int64#5,<c0=int64#11
+# asm 2: add <mulr8=%r8,<c0=%r13
+add %r8,%r13
+
+# qhasm: carry? c1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c1=int64#12
+# asm 2: adc <mulzero=%rdx,<c1=%r14
+adc %rdx,%r14
+
+# qhasm: carry? c2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c2=int64#13
+# asm 2: adc <mulzero=%rdx,<c2=%r15
+adc %rdx,%r15
+
+# qhasm: carry? c3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c3=int64#14
+# asm 2: adc <mulzero=%rdx,<c3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: c0 += mulzero
+# asm 1: add <mulzero=int64#3,<c0=int64#11
+# asm 2: add <mulzero=%rdx,<c0=%r13
+add %rdx,%r13
+
+# qhasm: c0_stack = c0
+# asm 1: movq <c0=int64#11,>c0_stack=stack64#8
+# asm 2: movq <c0=%r13,>c0_stack=56(%rsp)
+movq %r13,56(%rsp)
+
+# qhasm: c1_stack = c1
+# asm 1: movq <c1=int64#12,>c1_stack=stack64#9
+# asm 2: movq <c1=%r14,>c1_stack=64(%rsp)
+movq %r14,64(%rsp)
+
+# qhasm: c2_stack = c2
+# asm 1: movq <c2=int64#13,>c2_stack=stack64#10
+# asm 2: movq <c2=%r15,>c2_stack=72(%rsp)
+movq %r15,72(%rsp)
+
+# qhasm: c3_stack = c3
+# asm 1: movq <c3=int64#14,>c3_stack=stack64#11
+# asm 2: movq <c3=%rbx,>c3_stack=80(%rsp)
+movq %rbx,80(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = *(uint64 *)(pp + 64)
+# asm 1: movq 64(<pp=int64#2),>mulx0=int64#10
+# asm 2: movq 64(<pp=%rsi),>mulx0=%r12
+movq 64(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: rt0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rt0=int64#11
+# asm 2: mov <mulrax=%rax,>rt0=%r13
+mov %rax,%r13
+
+# qhasm: rt1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rt1=int64#12
+# asm 2: mov <mulrdx=%rdx,>rt1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rt1 += mulrax
+# asm 1: add <mulrax=int64#7,<rt1=int64#12
+# asm 2: add <mulrax=%rax,<rt1=%r14
+add %rax,%r14
+
+# qhasm: rt2 = 0
+# asm 1: mov $0,>rt2=int64#13
+# asm 2: mov $0,>rt2=%r15
+mov $0,%r15
+
+# qhasm: rt2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rt2=int64#13
+# asm 2: adc <mulrdx=%rdx,<rt2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#13
+# asm 2: add <mulrax=%rax,<rt2=%r15
+add %rax,%r15
+
+# qhasm: rt3 = 0
+# asm 1: mov $0,>rt3=int64#14
+# asm 2: mov $0,>rt3=%rbx
+mov $0,%rbx
+
+# qhasm: rt3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rt3=int64#14
+# asm 2: adc <mulrdx=%rdx,<rt3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#14
+# asm 2: add <mulrax=%rax,<rt3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = *(uint64 *)(pp + 72)
+# asm 1: movq 72(<pp=int64#2),>mulx1=int64#10
+# asm 2: movq 72(<pp=%rsi),>mulx1=%r12
+movq 72(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rt1 += mulrax
+# asm 1: add <mulrax=int64#7,<rt1=int64#12
+# asm 2: add <mulrax=%rax,<rt1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#13
+# asm 2: add <mulrax=%rax,<rt2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt2 += mulc
+# asm 1: add <mulc=int64#15,<rt2=int64#13
+# asm 2: add <mulc=%rbp,<rt2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#14
+# asm 2: add <mulrax=%rax,<rt3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt3 += mulc
+# asm 1: add <mulc=int64#15,<rt3=int64#14
+# asm 2: add <mulc=%rbp,<rt3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>mulx2=int64#10
+# asm 2: movq 80(<pp=%rsi),>mulx2=%r12
+movq 80(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#13
+# asm 2: add <mulrax=%rax,<rt2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#14
+# asm 2: add <mulrax=%rax,<rt3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt3 += mulc
+# asm 1: add <mulc=int64#15,<rt3=int64#14
+# asm 2: add <mulc=%rbp,<rt3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>mulx3=int64#2
+# asm 2: movq 88(<pp=%rsi),>mulx3=%rsi
+movq 88(%rsi),%rsi
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#14
+# asm 2: add <mulrax=%rax,<rt3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#10
+# asm 2: mov $0,>mulc=%r12
+mov $0,%r12
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#10
+# asm 2: adc <mulrdx=%rdx,<mulc=%r12
+adc %rdx,%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#10,<mulr4=int64#5
+# asm 2: add <mulc=%r12,<mulr4=%r8
+add %r12,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#10
+# asm 2: mov $0,>mulc=%r12
+mov $0,%r12
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#10
+# asm 2: adc <mulrdx=%rdx,<mulc=%r12
+adc %rdx,%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#10,<mulr5=int64#6
+# asm 2: add <mulc=%r12,<mulr5=%r9
+add %r12,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#10
+# asm 2: mov $0,>mulc=%r12
+mov $0,%r12
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#10
+# asm 2: adc <mulrdx=%rdx,<mulc=%r12
+adc %rdx,%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#10,<mulr6=int64#8
+# asm 2: add <mulc=%r12,<mulr6=%r10
+add %r12,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#2
+# asm 2: mov <mulrax=%rax,>mulr4=%rsi
+mov %rax,%rsi
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#4
+# asm 2: mov <mulrdx=%rdx,>mulr5=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#6
+# asm 2: add <mulrax=%rax,<mulr7=%r9
+add %rax,%r9
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rt0 += mulr4
+# asm 1: add <mulr4=int64#2,<rt0=int64#11
+# asm 2: add <mulr4=%rsi,<rt0=%r13
+add %rsi,%r13
+
+# qhasm: carry? rt1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#4,<rt1=int64#12
+# asm 2: adc <mulr5=%rcx,<rt1=%r14
+adc %rcx,%r14
+
+# qhasm: carry? rt2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#5,<rt2=int64#13
+# asm 2: adc <mulr6=%r8,<rt2=%r15
+adc %r8,%r15
+
+# qhasm: carry? rt3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#6,<rt3=int64#14
+# asm 2: adc <mulr7=%r9,<rt3=%rbx
+adc %r9,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#2
+# asm 2: mov $0,>mulzero=%rsi
+mov $0,%rsi
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulr8=int64#7
+# asm 2: adc <mulzero=%rsi,<mulr8=%rax
+adc %rsi,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#3
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? rt0 += mulr8
+# asm 1: add <mulr8=int64#3,<rt0=int64#11
+# asm 2: add <mulr8=%rdx,<rt0=%r13
+add %rdx,%r13
+
+# qhasm: carry? rt1 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt1=int64#12
+# asm 2: adc <mulzero=%rsi,<rt1=%r14
+adc %rsi,%r14
+
+# qhasm: carry? rt2 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt2=int64#13
+# asm 2: adc <mulzero=%rsi,<rt2=%r15
+adc %rsi,%r15
+
+# qhasm: carry? rt3 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt3=int64#14
+# asm 2: adc <mulzero=%rsi,<rt3=%rbx
+adc %rsi,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulzero=int64#2
+# asm 2: adc <mulzero=%rsi,<mulzero=%rsi
+adc %rsi,%rsi
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#2,>mulzero=int64#2
+# asm 2: imulq $38,<mulzero=%rsi,>mulzero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: rt0 += mulzero
+# asm 1: add <mulzero=int64#2,<rt0=int64#11
+# asm 2: add <mulzero=%rsi,<rt0=%r13
+add %rsi,%r13
+
+# qhasm: carry? rt0 += rt0
+# asm 1: add <rt0=int64#11,<rt0=int64#11
+# asm 2: add <rt0=%r13,<rt0=%r13
+add %r13,%r13
+
+# qhasm: carry? rt1 += rt1 + carry
+# asm 1: adc <rt1=int64#12,<rt1=int64#12
+# asm 2: adc <rt1=%r14,<rt1=%r14
+adc %r14,%r14
+
+# qhasm: carry? rt2 += rt2 + carry
+# asm 1: adc <rt2=int64#13,<rt2=int64#13
+# asm 2: adc <rt2=%r15,<rt2=%r15
+adc %r15,%r15
+
+# qhasm: carry? rt3 += rt3 + carry
+# asm 1: adc <rt3=int64#14,<rt3=int64#14
+# asm 2: adc <rt3=%rbx,<rt3=%rbx
+adc %rbx,%rbx
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#2
+# asm 2: mov $0,>addt0=%rsi
+mov $0,%rsi
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#3
+# asm 2: mov $38,>addt1=%rdx
+mov $38,%rdx
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#2,<addt1=int64#3
+# asm 2: cmovae <addt0=%rsi,<addt1=%rdx
+cmovae %rsi,%rdx
+
+# qhasm: carry? rt0 += addt1
+# asm 1: add <addt1=int64#3,<rt0=int64#11
+# asm 2: add <addt1=%rdx,<rt0=%r13
+add %rdx,%r13
+
+# qhasm: carry? rt1 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rt1=int64#12
+# asm 2: adc <addt0=%rsi,<rt1=%r14
+adc %rsi,%r14
+
+# qhasm: carry? rt2 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rt2=int64#13
+# asm 2: adc <addt0=%rsi,<rt2=%r15
+adc %rsi,%r15
+
+# qhasm: carry? rt3 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rt3=int64#14
+# asm 2: adc <addt0=%rsi,<rt3=%rbx
+adc %rsi,%rbx
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#3,<addt0=int64#2
+# asm 2: cmovc <addt1=%rdx,<addt0=%rsi
+cmovc %rdx,%rsi
+
+# qhasm: rt0 += addt0
+# asm 1: add <addt0=int64#2,<rt0=int64#11
+# asm 2: add <addt0=%rsi,<rt0=%r13
+add %rsi,%r13
+
+# qhasm: rz0 = rt0
+# asm 1: mov <rt0=int64#11,>rz0=int64#2
+# asm 2: mov <rt0=%r13,>rz0=%rsi
+mov %r13,%rsi
+
+# qhasm: rz1 = rt1
+# asm 1: mov <rt1=int64#12,>rz1=int64#3
+# asm 2: mov <rt1=%r14,>rz1=%rdx
+mov %r14,%rdx
+
+# qhasm: rz2 = rt2
+# asm 1: mov <rt2=int64#13,>rz2=int64#4
+# asm 2: mov <rt2=%r15,>rz2=%rcx
+mov %r15,%rcx
+
+# qhasm: rz3 = rt3
+# asm 1: mov <rt3=int64#14,>rz3=int64#5
+# asm 2: mov <rt3=%rbx,>rz3=%r8
+mov %rbx,%r8
+
+# qhasm: carry? rz0 += c0_stack
+# asm 1: addq <c0_stack=stack64#8,<rz0=int64#2
+# asm 2: addq <c0_stack=56(%rsp),<rz0=%rsi
+addq 56(%rsp),%rsi
+
+# qhasm: carry? rz1 += c1_stack + carry
+# asm 1: adcq <c1_stack=stack64#9,<rz1=int64#3
+# asm 2: adcq <c1_stack=64(%rsp),<rz1=%rdx
+adcq 64(%rsp),%rdx
+
+# qhasm: carry? rz2 += c2_stack + carry
+# asm 1: adcq <c2_stack=stack64#10,<rz2=int64#4
+# asm 2: adcq <c2_stack=72(%rsp),<rz2=%rcx
+adcq 72(%rsp),%rcx
+
+# qhasm: carry? rz3 += c3_stack + carry
+# asm 1: adcq <c3_stack=stack64#11,<rz3=int64#5
+# asm 2: adcq <c3_stack=80(%rsp),<rz3=%r8
+adcq 80(%rsp),%r8
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#6
+# asm 2: mov $0,>addt0=%r9
+mov $0,%r9
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#7
+# asm 2: mov $38,>addt1=%rax
+mov $38,%rax
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#6,<addt1=int64#7
+# asm 2: cmovae <addt0=%r9,<addt1=%rax
+cmovae %r9,%rax
+
+# qhasm: carry? rz0 += addt1
+# asm 1: add <addt1=int64#7,<rz0=int64#2
+# asm 2: add <addt1=%rax,<rz0=%rsi
+add %rax,%rsi
+
+# qhasm: carry? rz1 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<rz1=int64#3
+# asm 2: adc <addt0=%r9,<rz1=%rdx
+adc %r9,%rdx
+
+# qhasm: carry? rz2 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<rz2=int64#4
+# asm 2: adc <addt0=%r9,<rz2=%rcx
+adc %r9,%rcx
+
+# qhasm: carry? rz3 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<rz3=int64#5
+# asm 2: adc <addt0=%r9,<rz3=%r8
+adc %r9,%r8
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#7,<addt0=int64#6
+# asm 2: cmovc <addt1=%rax,<addt0=%r9
+cmovc %rax,%r9
+
+# qhasm: rz0 += addt0
+# asm 1: add <addt0=int64#6,<rz0=int64#2
+# asm 2: add <addt0=%r9,<rz0=%rsi
+add %r9,%rsi
+
+# qhasm: carry? rt0 -= c0_stack
+# asm 1: subq <c0_stack=stack64#8,<rt0=int64#11
+# asm 2: subq <c0_stack=56(%rsp),<rt0=%r13
+subq 56(%rsp),%r13
+
+# qhasm: carry? rt1 -= c1_stack - carry
+# asm 1: sbbq <c1_stack=stack64#9,<rt1=int64#12
+# asm 2: sbbq <c1_stack=64(%rsp),<rt1=%r14
+sbbq 64(%rsp),%r14
+
+# qhasm: carry? rt2 -= c2_stack - carry
+# asm 1: sbbq <c2_stack=stack64#10,<rt2=int64#13
+# asm 2: sbbq <c2_stack=72(%rsp),<rt2=%r15
+sbbq 72(%rsp),%r15
+
+# qhasm: carry? rt3 -= c3_stack - carry
+# asm 1: sbbq <c3_stack=stack64#11,<rt3=int64#14
+# asm 2: sbbq <c3_stack=80(%rsp),<rt3=%rbx
+sbbq 80(%rsp),%rbx
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#6
+# asm 2: mov $0,>subt0=%r9
+mov $0,%r9
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#7
+# asm 2: mov $38,>subt1=%rax
+mov $38,%rax
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#6,<subt1=int64#7
+# asm 2: cmovae <subt0=%r9,<subt1=%rax
+cmovae %r9,%rax
+
+# qhasm: carry? rt0 -= subt1
+# asm 1: sub <subt1=int64#7,<rt0=int64#11
+# asm 2: sub <subt1=%rax,<rt0=%r13
+sub %rax,%r13
+
+# qhasm: carry? rt1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#6,<rt1=int64#12
+# asm 2: sbb <subt0=%r9,<rt1=%r14
+sbb %r9,%r14
+
+# qhasm: carry? rt2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#6,<rt2=int64#13
+# asm 2: sbb <subt0=%r9,<rt2=%r15
+sbb %r9,%r15
+
+# qhasm: carry? rt3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#6,<rt3=int64#14
+# asm 2: sbb <subt0=%r9,<rt3=%rbx
+sbb %r9,%rbx
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#7,<subt0=int64#6
+# asm 2: cmovc <subt1=%rax,<subt0=%r9
+cmovc %rax,%r9
+
+# qhasm: rt0 -= subt0
+# asm 1: sub <subt0=int64#6,<rt0=int64#11
+# asm 2: sub <subt0=%r9,<rt0=%r13
+sub %r9,%r13
+
+# qhasm: *(uint64 *)(rp + 32) = rz0
+# asm 1: movq <rz0=int64#2,32(<rp=int64#1)
+# asm 2: movq <rz0=%rsi,32(<rp=%rdi)
+movq %rsi,32(%rdi)
+
+# qhasm: *(uint64 *)(rp + 40) = rz1
+# asm 1: movq <rz1=int64#3,40(<rp=int64#1)
+# asm 2: movq <rz1=%rdx,40(<rp=%rdi)
+movq %rdx,40(%rdi)
+
+# qhasm: *(uint64 *)(rp + 48) = rz2
+# asm 1: movq <rz2=int64#4,48(<rp=int64#1)
+# asm 2: movq <rz2=%rcx,48(<rp=%rdi)
+movq %rcx,48(%rdi)
+
+# qhasm: *(uint64 *)(rp + 56) = rz3
+# asm 1: movq <rz3=int64#5,56(<rp=int64#1)
+# asm 2: movq <rz3=%r8,56(<rp=%rdi)
+movq %r8,56(%rdi)
+
+# qhasm: *(uint64 *)(rp + 96) = rt0
+# asm 1: movq <rt0=int64#11,96(<rp=int64#1)
+# asm 2: movq <rt0=%r13,96(<rp=%rdi)
+movq %r13,96(%rdi)
+
+# qhasm: *(uint64 *)(rp + 104) = rt1
+# asm 1: movq <rt1=int64#12,104(<rp=int64#1)
+# asm 2: movq <rt1=%r14,104(<rp=%rdi)
+movq %r14,104(%rdi)
+
+# qhasm: *(uint64 *)(rp + 112) = rt2
+# asm 1: movq <rt2=int64#13,112(<rp=int64#1)
+# asm 2: movq <rt2=%r15,112(<rp=%rdi)
+movq %r15,112(%rdi)
+
+# qhasm: *(uint64 *)(rp + 120) = rt3
+# asm 1: movq <rt3=int64#14,120(<rp=int64#1)
+# asm 2: movq <rt3=%rbx,120(<rp=%rdi)
+movq %rbx,120(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/ge25519_scalarmult_base.c b/ext/ed25519-amd64-asm/ge25519_scalarmult_base.c
new file mode 100644
index 00000000..986abaf6
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_scalarmult_base.c
@@ -0,0 +1,68 @@
+#include "fe25519.h"
+#include "sc25519.h"
+#include "ge25519.h"
+
+/* Multiples of the base point in Niels' representation */
+static const ge25519_niels ge25519_base_multiples_niels[] = {
+#ifdef SMALLTABLES
+#include "ge25519_base_niels_smalltables.data"
+#else
+#include "ge25519_base_niels.data"
+#endif
+};
+
+/* d */
+static const fe25519 ecd = {{0x75EB4DCA135978A3, 0x00700A4D4141D8AB, 0x8CC740797779E898, 0x52036CEE2B6FFE73}};
+
+void ge25519_scalarmult_base(ge25519_p3 *r, const sc25519 *s)
+{
+ signed char b[64];
+ int i;
+ ge25519_niels t;
+ fe25519 d;
+
+ sc25519_window4(b,s);
+
+#ifdef SMALLTABLES
+ ge25519_p1p1 tp1p1;
+ choose_t((ge25519_niels *)r, 0, (signed long long) b[1], ge25519_base_multiples_niels);
+ fe25519_sub(&d, &r->y, &r->x);
+ fe25519_add(&r->y, &r->y, &r->x);
+ r->x = d;
+ r->t = r->z;
+ fe25519_setint(&r->z,2);
+ for(i=3;i<64;i+=2)
+ {
+ choose_t(&t, (unsigned long long) i/2, (signed long long) b[i], ge25519_base_multiples_niels);
+ ge25519_nielsadd2(r, &t);
+ }
+ ge25519_dbl_p1p1(&tp1p1,(ge25519_p2 *)r);
+ ge25519_p1p1_to_p2((ge25519_p2 *)r, &tp1p1);
+ ge25519_dbl_p1p1(&tp1p1,(ge25519_p2 *)r);
+ ge25519_p1p1_to_p2((ge25519_p2 *)r, &tp1p1);
+ ge25519_dbl_p1p1(&tp1p1,(ge25519_p2 *)r);
+ ge25519_p1p1_to_p2((ge25519_p2 *)r, &tp1p1);
+ ge25519_dbl_p1p1(&tp1p1,(ge25519_p2 *)r);
+ ge25519_p1p1_to_p3(r, &tp1p1);
+ choose_t(&t, (unsigned long long) 0, (signed long long) b[0], ge25519_base_multiples_niels);
+ fe25519_mul(&t.t2d, &t.t2d, &ecd);
+ ge25519_nielsadd2(r, &t);
+ for(i=2;i<64;i+=2)
+ {
+ choose_t(&t, (unsigned long long) i/2, (signed long long) b[i], ge25519_base_multiples_niels);
+ ge25519_nielsadd2(r, &t);
+ }
+#else
+ choose_t((ge25519_niels *)r, 0, (signed long long) b[0], ge25519_base_multiples_niels);
+ fe25519_sub(&d, &r->y, &r->x);
+ fe25519_add(&r->y, &r->y, &r->x);
+ r->x = d;
+ r->t = r->z;
+ fe25519_setint(&r->z,2);
+ for(i=1;i<64;i++)
+ {
+ choose_t(&t, (unsigned long long) i, (signed long long) b[i], ge25519_base_multiples_niels);
+ ge25519_nielsadd2(r, &t);
+ }
+#endif
+}
diff --git a/ext/ed25519-amd64-asm/ge25519_unpackneg.c b/ext/ed25519-amd64-asm/ge25519_unpackneg.c
new file mode 100644
index 00000000..ff16fd20
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_unpackneg.c
@@ -0,0 +1,60 @@
+#include "fe25519.h"
+#include "ge25519.h"
+
+/* d */
+static const fe25519 ecd = {{0x75EB4DCA135978A3, 0x00700A4D4141D8AB, 0x8CC740797779E898, 0x52036CEE2B6FFE73}};
+/* sqrt(-1) */
+static const fe25519 sqrtm1 = {{0xC4EE1B274A0EA0B0, 0x2F431806AD2FE478, 0x2B4D00993DFBD7A7, 0x2B8324804FC1DF0B}};
+
+/* return 0 on success, -1 otherwise */
+int ge25519_unpackneg_vartime(ge25519_p3 *r, const unsigned char p[32])
+{
+ fe25519 t, chk, num, den, den2, den4, den6;
+ unsigned char par = p[31] >> 7;
+
+ fe25519_setint(&r->z,1);
+ fe25519_unpack(&r->y, p);
+ fe25519_square(&num, &r->y); /* x = y^2 */
+ fe25519_mul(&den, &num, &ecd); /* den = dy^2 */
+ fe25519_sub(&num, &num, &r->z); /* x = y^2-1 */
+ fe25519_add(&den, &r->z, &den); /* den = dy^2+1 */
+
+ /* Computation of sqrt(num/den)
+ 1.: computation of num^((p-5)/8)*den^((7p-35)/8) = (num*den^7)^((p-5)/8)
+ */
+ fe25519_square(&den2, &den);
+ fe25519_square(&den4, &den2);
+ fe25519_mul(&den6, &den4, &den2);
+ fe25519_mul(&t, &den6, &num);
+ fe25519_mul(&t, &t, &den);
+
+ fe25519_pow2523(&t, &t);
+ /* 2. computation of r->x = t * num * den^3
+ */
+ fe25519_mul(&t, &t, &num);
+ fe25519_mul(&t, &t, &den);
+ fe25519_mul(&t, &t, &den);
+ fe25519_mul(&r->x, &t, &den);
+
+ /* 3. Check whether sqrt computation gave correct result, multiply by sqrt(-1) if not:
+ */
+ fe25519_square(&chk, &r->x);
+ fe25519_mul(&chk, &chk, &den);
+ if (!fe25519_iseq_vartime(&chk, &num))
+ fe25519_mul(&r->x, &r->x, &sqrtm1);
+
+ /* 4. Now we have one of the two square roots, except if input was not a square
+ */
+ fe25519_square(&chk, &r->x);
+ fe25519_mul(&chk, &chk, &den);
+ if (!fe25519_iseq_vartime(&chk, &num))
+ return -1;
+
+ /* 5. Choose the desired square root according to parity:
+ */
+ if(fe25519_getparity(&r->x) != (1-par))
+ fe25519_neg(&r->x, &r->x);
+
+ fe25519_mul(&r->t, &r->x, &r->y);
+ return 0;
+}
diff --git a/ext/ed25519-amd64-asm/heap_rootreplaced.s b/ext/ed25519-amd64-asm/heap_rootreplaced.s
new file mode 100644
index 00000000..8fe385b4
--- /dev/null
+++ b/ext/ed25519-amd64-asm/heap_rootreplaced.s
@@ -0,0 +1,476 @@
+
+# qhasm: int64 hp
+
+# qhasm: int64 hlen
+
+# qhasm: int64 sp
+
+# qhasm: int64 pp
+
+# qhasm: input hp
+
+# qhasm: input hlen
+
+# qhasm: input sp
+
+# qhasm: int64 prc
+
+# qhasm: int64 plc
+
+# qhasm: int64 pc
+
+# qhasm: int64 d
+
+# qhasm: int64 spp
+
+# qhasm: int64 sprc
+
+# qhasm: int64 spc
+
+# qhasm: int64 c0
+
+# qhasm: int64 c1
+
+# qhasm: int64 c2
+
+# qhasm: int64 c3
+
+# qhasm: int64 t0
+
+# qhasm: int64 t1
+
+# qhasm: int64 t2
+
+# qhasm: int64 t3
+
+# qhasm: int64 p0
+
+# qhasm: int64 p1
+
+# qhasm: int64 p2
+
+# qhasm: int64 p3
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_heap_rootreplaced
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_heap_rootreplaced
+.globl crypto_sign_ed25519_amd64_64_heap_rootreplaced
+_crypto_sign_ed25519_amd64_64_heap_rootreplaced:
+crypto_sign_ed25519_amd64_64_heap_rootreplaced:
+mov %rsp,%r11
+and $31,%r11
+add $64,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: pp = 0
+# asm 1: mov $0,>pp=int64#4
+# asm 2: mov $0,>pp=%rcx
+mov $0,%rcx
+
+# qhasm: siftdownloop:
+._siftdownloop:
+
+# qhasm: prc = pp
+# asm 1: mov <pp=int64#4,>prc=int64#5
+# asm 2: mov <pp=%rcx,>prc=%r8
+mov %rcx,%r8
+
+# qhasm: prc *= 2
+# asm 1: imulq $2,<prc=int64#5,>prc=int64#5
+# asm 2: imulq $2,<prc=%r8,>prc=%r8
+imulq $2,%r8,%r8
+
+# qhasm: pc = prc
+# asm 1: mov <prc=int64#5,>pc=int64#6
+# asm 2: mov <prc=%r8,>pc=%r9
+mov %r8,%r9
+
+# qhasm: prc += 2
+# asm 1: add $2,<prc=int64#5
+# asm 2: add $2,<prc=%r8
+add $2,%r8
+
+# qhasm: pc += 1
+# asm 1: add $1,<pc=int64#6
+# asm 2: add $1,<pc=%r9
+add $1,%r9
+
+# qhasm: unsigned>? hlen - prc
+# asm 1: cmp <prc=int64#5,<hlen=int64#2
+# asm 2: cmp <prc=%r8,<hlen=%rsi
+cmp %r8,%rsi
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftuploop if !unsigned>
+jbe ._siftuploop
+
+# qhasm: sprc = *(uint64 *)(hp + prc * 8)
+# asm 1: movq (<hp=int64#1,<prc=int64#5,8),>sprc=int64#7
+# asm 2: movq (<hp=%rdi,<prc=%r8,8),>sprc=%rax
+movq (%rdi,%r8,8),%rax
+
+# qhasm: sprc <<= 5
+# asm 1: shl $5,<sprc=int64#7
+# asm 2: shl $5,<sprc=%rax
+shl $5,%rax
+
+# qhasm: sprc += sp
+# asm 1: add <sp=int64#3,<sprc=int64#7
+# asm 2: add <sp=%rdx,<sprc=%rax
+add %rdx,%rax
+
+# qhasm: spc = *(uint64 *)(hp + pc * 8)
+# asm 1: movq (<hp=int64#1,<pc=int64#6,8),>spc=int64#8
+# asm 2: movq (<hp=%rdi,<pc=%r9,8),>spc=%r10
+movq (%rdi,%r9,8),%r10
+
+# qhasm: spc <<= 5
+# asm 1: shl $5,<spc=int64#8
+# asm 2: shl $5,<spc=%r10
+shl $5,%r10
+
+# qhasm: spc += sp
+# asm 1: add <sp=int64#3,<spc=int64#8
+# asm 2: add <sp=%rdx,<spc=%r10
+add %rdx,%r10
+
+# qhasm: c0 = *(uint64 *)(spc + 0)
+# asm 1: movq 0(<spc=int64#8),>c0=int64#9
+# asm 2: movq 0(<spc=%r10),>c0=%r11
+movq 0(%r10),%r11
+
+# qhasm: c1 = *(uint64 *)(spc + 8)
+# asm 1: movq 8(<spc=int64#8),>c1=int64#10
+# asm 2: movq 8(<spc=%r10),>c1=%r12
+movq 8(%r10),%r12
+
+# qhasm: c2 = *(uint64 *)(spc + 16)
+# asm 1: movq 16(<spc=int64#8),>c2=int64#11
+# asm 2: movq 16(<spc=%r10),>c2=%r13
+movq 16(%r10),%r13
+
+# qhasm: c3 = *(uint64 *)(spc + 24)
+# asm 1: movq 24(<spc=int64#8),>c3=int64#12
+# asm 2: movq 24(<spc=%r10),>c3=%r14
+movq 24(%r10),%r14
+
+# qhasm: carry? c0 -= *(uint64 *)(sprc + 0)
+# asm 1: subq 0(<sprc=int64#7),<c0=int64#9
+# asm 2: subq 0(<sprc=%rax),<c0=%r11
+subq 0(%rax),%r11
+
+# qhasm: carry? c1 -= *(uint64 *)(sprc + 8) - carry
+# asm 1: sbbq 8(<sprc=int64#7),<c1=int64#10
+# asm 2: sbbq 8(<sprc=%rax),<c1=%r12
+sbbq 8(%rax),%r12
+
+# qhasm: carry? c2 -= *(uint64 *)(sprc + 16) - carry
+# asm 1: sbbq 16(<sprc=int64#7),<c2=int64#11
+# asm 2: sbbq 16(<sprc=%rax),<c2=%r13
+sbbq 16(%rax),%r13
+
+# qhasm: carry? c3 -= *(uint64 *)(sprc + 24) - carry
+# asm 1: sbbq 24(<sprc=int64#7),<c3=int64#12
+# asm 2: sbbq 24(<sprc=%rax),<c3=%r14
+sbbq 24(%rax),%r14
+
+# qhasm: pc = prc if carry
+# asm 1: cmovc <prc=int64#5,<pc=int64#6
+# asm 2: cmovc <prc=%r8,<pc=%r9
+cmovc %r8,%r9
+
+# qhasm: spc = sprc if carry
+# asm 1: cmovc <sprc=int64#7,<spc=int64#8
+# asm 2: cmovc <sprc=%rax,<spc=%r10
+cmovc %rax,%r10
+
+# qhasm: spc -= sp
+# asm 1: sub <sp=int64#3,<spc=int64#8
+# asm 2: sub <sp=%rdx,<spc=%r10
+sub %rdx,%r10
+
+# qhasm: (uint64) spc >>= 5
+# asm 1: shr $5,<spc=int64#8
+# asm 2: shr $5,<spc=%r10
+shr $5,%r10
+
+# qhasm: spp = *(uint64 *)(hp + pp * 8)
+# asm 1: movq (<hp=int64#1,<pp=int64#4,8),>spp=int64#5
+# asm 2: movq (<hp=%rdi,<pp=%rcx,8),>spp=%r8
+movq (%rdi,%rcx,8),%r8
+
+# qhasm: *(uint64 *)(hp + pp * 8) = spc
+# asm 1: movq <spc=int64#8,(<hp=int64#1,<pp=int64#4,8)
+# asm 2: movq <spc=%r10,(<hp=%rdi,<pp=%rcx,8)
+movq %r10,(%rdi,%rcx,8)
+
+# qhasm: *(uint64 *)(hp + pc * 8) = spp
+# asm 1: movq <spp=int64#5,(<hp=int64#1,<pc=int64#6,8)
+# asm 2: movq <spp=%r8,(<hp=%rdi,<pc=%r9,8)
+movq %r8,(%rdi,%r9,8)
+
+# qhasm: pp = pc
+# asm 1: mov <pc=int64#6,>pp=int64#4
+# asm 2: mov <pc=%r9,>pp=%rcx
+mov %r9,%rcx
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftdownloop
+jmp ._siftdownloop
+
+# qhasm: siftuploop:
+._siftuploop:
+
+# qhasm: pc = pp
+# asm 1: mov <pp=int64#4,>pc=int64#2
+# asm 2: mov <pp=%rcx,>pc=%rsi
+mov %rcx,%rsi
+
+# qhasm: pp -= 1
+# asm 1: sub $1,<pp=int64#4
+# asm 2: sub $1,<pp=%rcx
+sub $1,%rcx
+
+# qhasm: (uint64) pp >>= 1
+# asm 1: shr $1,<pp=int64#4
+# asm 2: shr $1,<pp=%rcx
+shr $1,%rcx
+
+# qhasm: unsigned>? pc - 0
+# asm 1: cmp $0,<pc=int64#2
+# asm 2: cmp $0,<pc=%rsi
+cmp $0,%rsi
+# comment:fp stack unchanged by jump
+
+# qhasm: goto end if !unsigned>
+jbe ._end
+
+# qhasm: spp = *(uint64 *)(hp + pp * 8)
+# asm 1: movq (<hp=int64#1,<pp=int64#4,8),>spp=int64#5
+# asm 2: movq (<hp=%rdi,<pp=%rcx,8),>spp=%r8
+movq (%rdi,%rcx,8),%r8
+
+# qhasm: spc = *(uint64 *)(hp + pc * 8)
+# asm 1: movq (<hp=int64#1,<pc=int64#2,8),>spc=int64#6
+# asm 2: movq (<hp=%rdi,<pc=%rsi,8),>spc=%r9
+movq (%rdi,%rsi,8),%r9
+
+# qhasm: spp <<= 5
+# asm 1: shl $5,<spp=int64#5
+# asm 2: shl $5,<spp=%r8
+shl $5,%r8
+
+# qhasm: spc <<= 5
+# asm 1: shl $5,<spc=int64#6
+# asm 2: shl $5,<spc=%r9
+shl $5,%r9
+
+# qhasm: spc += sp
+# asm 1: add <sp=int64#3,<spc=int64#6
+# asm 2: add <sp=%rdx,<spc=%r9
+add %rdx,%r9
+
+# qhasm: spp += sp
+# asm 1: add <sp=int64#3,<spp=int64#5
+# asm 2: add <sp=%rdx,<spp=%r8
+add %rdx,%r8
+
+# qhasm: c0 = *(uint64 *)(spc + 0)
+# asm 1: movq 0(<spc=int64#6),>c0=int64#7
+# asm 2: movq 0(<spc=%r9),>c0=%rax
+movq 0(%r9),%rax
+
+# qhasm: c1 = *(uint64 *)(spc + 8)
+# asm 1: movq 8(<spc=int64#6),>c1=int64#8
+# asm 2: movq 8(<spc=%r9),>c1=%r10
+movq 8(%r9),%r10
+
+# qhasm: c2 = *(uint64 *)(spc + 16)
+# asm 1: movq 16(<spc=int64#6),>c2=int64#9
+# asm 2: movq 16(<spc=%r9),>c2=%r11
+movq 16(%r9),%r11
+
+# qhasm: c3 = *(uint64 *)(spc + 24)
+# asm 1: movq 24(<spc=int64#6),>c3=int64#10
+# asm 2: movq 24(<spc=%r9),>c3=%r12
+movq 24(%r9),%r12
+
+# qhasm: carry? c0 -= *(uint64 *)(spp + 0)
+# asm 1: subq 0(<spp=int64#5),<c0=int64#7
+# asm 2: subq 0(<spp=%r8),<c0=%rax
+subq 0(%r8),%rax
+
+# qhasm: carry? c1 -= *(uint64 *)(spp + 8) - carry
+# asm 1: sbbq 8(<spp=int64#5),<c1=int64#8
+# asm 2: sbbq 8(<spp=%r8),<c1=%r10
+sbbq 8(%r8),%r10
+
+# qhasm: carry? c2 -= *(uint64 *)(spp + 16) - carry
+# asm 1: sbbq 16(<spp=int64#5),<c2=int64#9
+# asm 2: sbbq 16(<spp=%r8),<c2=%r11
+sbbq 16(%r8),%r11
+
+# qhasm: carry? c3 -= *(uint64 *)(spp + 24) - carry
+# asm 1: sbbq 24(<spp=int64#5),<c3=int64#10
+# asm 2: sbbq 24(<spp=%r8),<c3=%r12
+sbbq 24(%r8),%r12
+# comment:fp stack unchanged by jump
+
+# qhasm: goto end if carry
+jc ._end
+
+# qhasm: spc -= sp
+# asm 1: sub <sp=int64#3,<spc=int64#6
+# asm 2: sub <sp=%rdx,<spc=%r9
+sub %rdx,%r9
+
+# qhasm: (uint64) spc >>= 5
+# asm 1: shr $5,<spc=int64#6
+# asm 2: shr $5,<spc=%r9
+shr $5,%r9
+
+# qhasm: spp -= sp
+# asm 1: sub <sp=int64#3,<spp=int64#5
+# asm 2: sub <sp=%rdx,<spp=%r8
+sub %rdx,%r8
+
+# qhasm: (uint64) spp >>= 5
+# asm 1: shr $5,<spp=int64#5
+# asm 2: shr $5,<spp=%r8
+shr $5,%r8
+
+# qhasm: *(uint64 *)(hp + pp * 8) = spc
+# asm 1: movq <spc=int64#6,(<hp=int64#1,<pp=int64#4,8)
+# asm 2: movq <spc=%r9,(<hp=%rdi,<pp=%rcx,8)
+movq %r9,(%rdi,%rcx,8)
+
+# qhasm: *(uint64 *)(hp + pc * 8) = spp
+# asm 1: movq <spp=int64#5,(<hp=int64#1,<pc=int64#2,8)
+# asm 2: movq <spp=%r8,(<hp=%rdi,<pc=%rsi,8)
+movq %r8,(%rdi,%rsi,8)
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftuploop
+jmp ._siftuploop
+
+# qhasm: end:
+._end:
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/heap_rootreplaced_1limb.s b/ext/ed25519-amd64-asm/heap_rootreplaced_1limb.s
new file mode 100644
index 00000000..488e9c52
--- /dev/null
+++ b/ext/ed25519-amd64-asm/heap_rootreplaced_1limb.s
@@ -0,0 +1,416 @@
+
+# qhasm: int64 hp
+
+# qhasm: int64 hlen
+
+# qhasm: int64 sp
+
+# qhasm: int64 pp
+
+# qhasm: input hp
+
+# qhasm: input hlen
+
+# qhasm: input sp
+
+# qhasm: int64 prc
+
+# qhasm: int64 plc
+
+# qhasm: int64 pc
+
+# qhasm: int64 d
+
+# qhasm: int64 spp
+
+# qhasm: int64 sprc
+
+# qhasm: int64 spc
+
+# qhasm: int64 c0
+
+# qhasm: int64 c1
+
+# qhasm: int64 c2
+
+# qhasm: int64 c3
+
+# qhasm: int64 t0
+
+# qhasm: int64 t1
+
+# qhasm: int64 t2
+
+# qhasm: int64 t3
+
+# qhasm: int64 p0
+
+# qhasm: int64 p1
+
+# qhasm: int64 p2
+
+# qhasm: int64 p3
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_heap_rootreplaced_1limb
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_heap_rootreplaced_1limb
+.globl crypto_sign_ed25519_amd64_64_heap_rootreplaced_1limb
+_crypto_sign_ed25519_amd64_64_heap_rootreplaced_1limb:
+crypto_sign_ed25519_amd64_64_heap_rootreplaced_1limb:
+mov %rsp,%r11
+and $31,%r11
+add $64,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: pp = 0
+# asm 1: mov $0,>pp=int64#4
+# asm 2: mov $0,>pp=%rcx
+mov $0,%rcx
+
+# qhasm: siftdownloop:
+._siftdownloop:
+
+# qhasm: prc = pp
+# asm 1: mov <pp=int64#4,>prc=int64#5
+# asm 2: mov <pp=%rcx,>prc=%r8
+mov %rcx,%r8
+
+# qhasm: prc *= 2
+# asm 1: imulq $2,<prc=int64#5,>prc=int64#5
+# asm 2: imulq $2,<prc=%r8,>prc=%r8
+imulq $2,%r8,%r8
+
+# qhasm: pc = prc
+# asm 1: mov <prc=int64#5,>pc=int64#6
+# asm 2: mov <prc=%r8,>pc=%r9
+mov %r8,%r9
+
+# qhasm: prc += 2
+# asm 1: add $2,<prc=int64#5
+# asm 2: add $2,<prc=%r8
+add $2,%r8
+
+# qhasm: pc += 1
+# asm 1: add $1,<pc=int64#6
+# asm 2: add $1,<pc=%r9
+add $1,%r9
+
+# qhasm: unsigned>? hlen - prc
+# asm 1: cmp <prc=int64#5,<hlen=int64#2
+# asm 2: cmp <prc=%r8,<hlen=%rsi
+cmp %r8,%rsi
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftuploop if !unsigned>
+jbe ._siftuploop
+
+# qhasm: sprc = *(uint64 *)(hp + prc * 8)
+# asm 1: movq (<hp=int64#1,<prc=int64#5,8),>sprc=int64#7
+# asm 2: movq (<hp=%rdi,<prc=%r8,8),>sprc=%rax
+movq (%rdi,%r8,8),%rax
+
+# qhasm: sprc <<= 5
+# asm 1: shl $5,<sprc=int64#7
+# asm 2: shl $5,<sprc=%rax
+shl $5,%rax
+
+# qhasm: sprc += sp
+# asm 1: add <sp=int64#3,<sprc=int64#7
+# asm 2: add <sp=%rdx,<sprc=%rax
+add %rdx,%rax
+
+# qhasm: spc = *(uint64 *)(hp + pc * 8)
+# asm 1: movq (<hp=int64#1,<pc=int64#6,8),>spc=int64#8
+# asm 2: movq (<hp=%rdi,<pc=%r9,8),>spc=%r10
+movq (%rdi,%r9,8),%r10
+
+# qhasm: spc <<= 5
+# asm 1: shl $5,<spc=int64#8
+# asm 2: shl $5,<spc=%r10
+shl $5,%r10
+
+# qhasm: spc += sp
+# asm 1: add <sp=int64#3,<spc=int64#8
+# asm 2: add <sp=%rdx,<spc=%r10
+add %rdx,%r10
+
+# qhasm: c0 = *(uint64 *)(spc + 0)
+# asm 1: movq 0(<spc=int64#8),>c0=int64#9
+# asm 2: movq 0(<spc=%r10),>c0=%r11
+movq 0(%r10),%r11
+
+# qhasm: carry? c0 -= *(uint64 *)(sprc + 0)
+# asm 1: subq 0(<sprc=int64#7),<c0=int64#9
+# asm 2: subq 0(<sprc=%rax),<c0=%r11
+subq 0(%rax),%r11
+
+# qhasm: pc = prc if carry
+# asm 1: cmovc <prc=int64#5,<pc=int64#6
+# asm 2: cmovc <prc=%r8,<pc=%r9
+cmovc %r8,%r9
+
+# qhasm: spc = sprc if carry
+# asm 1: cmovc <sprc=int64#7,<spc=int64#8
+# asm 2: cmovc <sprc=%rax,<spc=%r10
+cmovc %rax,%r10
+
+# qhasm: spc -= sp
+# asm 1: sub <sp=int64#3,<spc=int64#8
+# asm 2: sub <sp=%rdx,<spc=%r10
+sub %rdx,%r10
+
+# qhasm: (uint64) spc >>= 5
+# asm 1: shr $5,<spc=int64#8
+# asm 2: shr $5,<spc=%r10
+shr $5,%r10
+
+# qhasm: spp = *(uint64 *)(hp + pp * 8)
+# asm 1: movq (<hp=int64#1,<pp=int64#4,8),>spp=int64#5
+# asm 2: movq (<hp=%rdi,<pp=%rcx,8),>spp=%r8
+movq (%rdi,%rcx,8),%r8
+
+# qhasm: *(uint64 *)(hp + pp * 8) = spc
+# asm 1: movq <spc=int64#8,(<hp=int64#1,<pp=int64#4,8)
+# asm 2: movq <spc=%r10,(<hp=%rdi,<pp=%rcx,8)
+movq %r10,(%rdi,%rcx,8)
+
+# qhasm: *(uint64 *)(hp + pc * 8) = spp
+# asm 1: movq <spp=int64#5,(<hp=int64#1,<pc=int64#6,8)
+# asm 2: movq <spp=%r8,(<hp=%rdi,<pc=%r9,8)
+movq %r8,(%rdi,%r9,8)
+
+# qhasm: pp = pc
+# asm 1: mov <pc=int64#6,>pp=int64#4
+# asm 2: mov <pc=%r9,>pp=%rcx
+mov %r9,%rcx
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftdownloop
+jmp ._siftdownloop
+
+# qhasm: siftuploop:
+._siftuploop:
+
+# qhasm: pc = pp
+# asm 1: mov <pp=int64#4,>pc=int64#2
+# asm 2: mov <pp=%rcx,>pc=%rsi
+mov %rcx,%rsi
+
+# qhasm: pp -= 1
+# asm 1: sub $1,<pp=int64#4
+# asm 2: sub $1,<pp=%rcx
+sub $1,%rcx
+
+# qhasm: (uint64) pp >>= 1
+# asm 1: shr $1,<pp=int64#4
+# asm 2: shr $1,<pp=%rcx
+shr $1,%rcx
+
+# qhasm: unsigned>? pc - 0
+# asm 1: cmp $0,<pc=int64#2
+# asm 2: cmp $0,<pc=%rsi
+cmp $0,%rsi
+# comment:fp stack unchanged by jump
+
+# qhasm: goto end if !unsigned>
+jbe ._end
+
+# qhasm: spp = *(uint64 *)(hp + pp * 8)
+# asm 1: movq (<hp=int64#1,<pp=int64#4,8),>spp=int64#5
+# asm 2: movq (<hp=%rdi,<pp=%rcx,8),>spp=%r8
+movq (%rdi,%rcx,8),%r8
+
+# qhasm: spc = *(uint64 *)(hp + pc * 8)
+# asm 1: movq (<hp=int64#1,<pc=int64#2,8),>spc=int64#6
+# asm 2: movq (<hp=%rdi,<pc=%rsi,8),>spc=%r9
+movq (%rdi,%rsi,8),%r9
+
+# qhasm: spp <<= 5
+# asm 1: shl $5,<spp=int64#5
+# asm 2: shl $5,<spp=%r8
+shl $5,%r8
+
+# qhasm: spc <<= 5
+# asm 1: shl $5,<spc=int64#6
+# asm 2: shl $5,<spc=%r9
+shl $5,%r9
+
+# qhasm: spc += sp
+# asm 1: add <sp=int64#3,<spc=int64#6
+# asm 2: add <sp=%rdx,<spc=%r9
+add %rdx,%r9
+
+# qhasm: spp += sp
+# asm 1: add <sp=int64#3,<spp=int64#5
+# asm 2: add <sp=%rdx,<spp=%r8
+add %rdx,%r8
+
+# qhasm: c0 = *(uint64 *)(spc + 0)
+# asm 1: movq 0(<spc=int64#6),>c0=int64#7
+# asm 2: movq 0(<spc=%r9),>c0=%rax
+movq 0(%r9),%rax
+
+# qhasm: carry? c0 -= *(uint64 *)(spp + 0)
+# asm 1: subq 0(<spp=int64#5),<c0=int64#7
+# asm 2: subq 0(<spp=%r8),<c0=%rax
+subq 0(%r8),%rax
+# comment:fp stack unchanged by jump
+
+# qhasm: goto end if carry
+jc ._end
+
+# qhasm: spc -= sp
+# asm 1: sub <sp=int64#3,<spc=int64#6
+# asm 2: sub <sp=%rdx,<spc=%r9
+sub %rdx,%r9
+
+# qhasm: (uint64) spc >>= 5
+# asm 1: shr $5,<spc=int64#6
+# asm 2: shr $5,<spc=%r9
+shr $5,%r9
+
+# qhasm: spp -= sp
+# asm 1: sub <sp=int64#3,<spp=int64#5
+# asm 2: sub <sp=%rdx,<spp=%r8
+sub %rdx,%r8
+
+# qhasm: (uint64) spp >>= 5
+# asm 1: shr $5,<spp=int64#5
+# asm 2: shr $5,<spp=%r8
+shr $5,%r8
+
+# qhasm: *(uint64 *)(hp + pp * 8) = spc
+# asm 1: movq <spc=int64#6,(<hp=int64#1,<pp=int64#4,8)
+# asm 2: movq <spc=%r9,(<hp=%rdi,<pp=%rcx,8)
+movq %r9,(%rdi,%rcx,8)
+
+# qhasm: *(uint64 *)(hp + pc * 8) = spp
+# asm 1: movq <spp=int64#5,(<hp=int64#1,<pc=int64#2,8)
+# asm 2: movq <spp=%r8,(<hp=%rdi,<pc=%rsi,8)
+movq %r8,(%rdi,%rsi,8)
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftuploop
+jmp ._siftuploop
+
+# qhasm: end:
+._end:
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/heap_rootreplaced_2limbs.s b/ext/ed25519-amd64-asm/heap_rootreplaced_2limbs.s
new file mode 100644
index 00000000..f9259184
--- /dev/null
+++ b/ext/ed25519-amd64-asm/heap_rootreplaced_2limbs.s
@@ -0,0 +1,436 @@
+
+# qhasm: int64 hp
+
+# qhasm: int64 hlen
+
+# qhasm: int64 sp
+
+# qhasm: int64 pp
+
+# qhasm: input hp
+
+# qhasm: input hlen
+
+# qhasm: input sp
+
+# qhasm: int64 prc
+
+# qhasm: int64 plc
+
+# qhasm: int64 pc
+
+# qhasm: int64 d
+
+# qhasm: int64 spp
+
+# qhasm: int64 sprc
+
+# qhasm: int64 spc
+
+# qhasm: int64 c0
+
+# qhasm: int64 c1
+
+# qhasm: int64 c2
+
+# qhasm: int64 c3
+
+# qhasm: int64 t0
+
+# qhasm: int64 t1
+
+# qhasm: int64 t2
+
+# qhasm: int64 t3
+
+# qhasm: int64 p0
+
+# qhasm: int64 p1
+
+# qhasm: int64 p2
+
+# qhasm: int64 p3
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_heap_rootreplaced_2limbs
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_heap_rootreplaced_2limbs
+.globl crypto_sign_ed25519_amd64_64_heap_rootreplaced_2limbs
+_crypto_sign_ed25519_amd64_64_heap_rootreplaced_2limbs:
+crypto_sign_ed25519_amd64_64_heap_rootreplaced_2limbs:
+mov %rsp,%r11
+and $31,%r11
+add $64,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: pp = 0
+# asm 1: mov $0,>pp=int64#4
+# asm 2: mov $0,>pp=%rcx
+mov $0,%rcx
+
+# qhasm: siftdownloop:
+._siftdownloop:
+
+# qhasm: prc = pp
+# asm 1: mov <pp=int64#4,>prc=int64#5
+# asm 2: mov <pp=%rcx,>prc=%r8
+mov %rcx,%r8
+
+# qhasm: prc *= 2
+# asm 1: imulq $2,<prc=int64#5,>prc=int64#5
+# asm 2: imulq $2,<prc=%r8,>prc=%r8
+imulq $2,%r8,%r8
+
+# qhasm: pc = prc
+# asm 1: mov <prc=int64#5,>pc=int64#6
+# asm 2: mov <prc=%r8,>pc=%r9
+mov %r8,%r9
+
+# qhasm: prc += 2
+# asm 1: add $2,<prc=int64#5
+# asm 2: add $2,<prc=%r8
+add $2,%r8
+
+# qhasm: pc += 1
+# asm 1: add $1,<pc=int64#6
+# asm 2: add $1,<pc=%r9
+add $1,%r9
+
+# qhasm: unsigned>? hlen - prc
+# asm 1: cmp <prc=int64#5,<hlen=int64#2
+# asm 2: cmp <prc=%r8,<hlen=%rsi
+cmp %r8,%rsi
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftuploop if !unsigned>
+jbe ._siftuploop
+
+# qhasm: sprc = *(uint64 *)(hp + prc * 8)
+# asm 1: movq (<hp=int64#1,<prc=int64#5,8),>sprc=int64#7
+# asm 2: movq (<hp=%rdi,<prc=%r8,8),>sprc=%rax
+movq (%rdi,%r8,8),%rax
+
+# qhasm: sprc <<= 5
+# asm 1: shl $5,<sprc=int64#7
+# asm 2: shl $5,<sprc=%rax
+shl $5,%rax
+
+# qhasm: sprc += sp
+# asm 1: add <sp=int64#3,<sprc=int64#7
+# asm 2: add <sp=%rdx,<sprc=%rax
+add %rdx,%rax
+
+# qhasm: spc = *(uint64 *)(hp + pc * 8)
+# asm 1: movq (<hp=int64#1,<pc=int64#6,8),>spc=int64#8
+# asm 2: movq (<hp=%rdi,<pc=%r9,8),>spc=%r10
+movq (%rdi,%r9,8),%r10
+
+# qhasm: spc <<= 5
+# asm 1: shl $5,<spc=int64#8
+# asm 2: shl $5,<spc=%r10
+shl $5,%r10
+
+# qhasm: spc += sp
+# asm 1: add <sp=int64#3,<spc=int64#8
+# asm 2: add <sp=%rdx,<spc=%r10
+add %rdx,%r10
+
+# qhasm: c0 = *(uint64 *)(spc + 0)
+# asm 1: movq 0(<spc=int64#8),>c0=int64#9
+# asm 2: movq 0(<spc=%r10),>c0=%r11
+movq 0(%r10),%r11
+
+# qhasm: c1 = *(uint64 *)(spc + 8)
+# asm 1: movq 8(<spc=int64#8),>c1=int64#10
+# asm 2: movq 8(<spc=%r10),>c1=%r12
+movq 8(%r10),%r12
+
+# qhasm: carry? c0 -= *(uint64 *)(sprc + 0)
+# asm 1: subq 0(<sprc=int64#7),<c0=int64#9
+# asm 2: subq 0(<sprc=%rax),<c0=%r11
+subq 0(%rax),%r11
+
+# qhasm: carry? c1 -= *(uint64 *)(sprc + 8) - carry
+# asm 1: sbbq 8(<sprc=int64#7),<c1=int64#10
+# asm 2: sbbq 8(<sprc=%rax),<c1=%r12
+sbbq 8(%rax),%r12
+
+# qhasm: pc = prc if carry
+# asm 1: cmovc <prc=int64#5,<pc=int64#6
+# asm 2: cmovc <prc=%r8,<pc=%r9
+cmovc %r8,%r9
+
+# qhasm: spc = sprc if carry
+# asm 1: cmovc <sprc=int64#7,<spc=int64#8
+# asm 2: cmovc <sprc=%rax,<spc=%r10
+cmovc %rax,%r10
+
+# qhasm: spc -= sp
+# asm 1: sub <sp=int64#3,<spc=int64#8
+# asm 2: sub <sp=%rdx,<spc=%r10
+sub %rdx,%r10
+
+# qhasm: (uint64) spc >>= 5
+# asm 1: shr $5,<spc=int64#8
+# asm 2: shr $5,<spc=%r10
+shr $5,%r10
+
+# qhasm: spp = *(uint64 *)(hp + pp * 8)
+# asm 1: movq (<hp=int64#1,<pp=int64#4,8),>spp=int64#5
+# asm 2: movq (<hp=%rdi,<pp=%rcx,8),>spp=%r8
+movq (%rdi,%rcx,8),%r8
+
+# qhasm: *(uint64 *)(hp + pp * 8) = spc
+# asm 1: movq <spc=int64#8,(<hp=int64#1,<pp=int64#4,8)
+# asm 2: movq <spc=%r10,(<hp=%rdi,<pp=%rcx,8)
+movq %r10,(%rdi,%rcx,8)
+
+# qhasm: *(uint64 *)(hp + pc * 8) = spp
+# asm 1: movq <spp=int64#5,(<hp=int64#1,<pc=int64#6,8)
+# asm 2: movq <spp=%r8,(<hp=%rdi,<pc=%r9,8)
+movq %r8,(%rdi,%r9,8)
+
+# qhasm: pp = pc
+# asm 1: mov <pc=int64#6,>pp=int64#4
+# asm 2: mov <pc=%r9,>pp=%rcx
+mov %r9,%rcx
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftdownloop
+jmp ._siftdownloop
+
+# qhasm: siftuploop:
+._siftuploop:
+
+# qhasm: pc = pp
+# asm 1: mov <pp=int64#4,>pc=int64#2
+# asm 2: mov <pp=%rcx,>pc=%rsi
+mov %rcx,%rsi
+
+# qhasm: pp -= 1
+# asm 1: sub $1,<pp=int64#4
+# asm 2: sub $1,<pp=%rcx
+sub $1,%rcx
+
+# qhasm: (uint64) pp >>= 1
+# asm 1: shr $1,<pp=int64#4
+# asm 2: shr $1,<pp=%rcx
+shr $1,%rcx
+
+# qhasm: unsigned>? pc - 0
+# asm 1: cmp $0,<pc=int64#2
+# asm 2: cmp $0,<pc=%rsi
+cmp $0,%rsi
+# comment:fp stack unchanged by jump
+
+# qhasm: goto end if !unsigned>
+jbe ._end
+
+# qhasm: spp = *(uint64 *)(hp + pp * 8)
+# asm 1: movq (<hp=int64#1,<pp=int64#4,8),>spp=int64#5
+# asm 2: movq (<hp=%rdi,<pp=%rcx,8),>spp=%r8
+movq (%rdi,%rcx,8),%r8
+
+# qhasm: spc = *(uint64 *)(hp + pc * 8)
+# asm 1: movq (<hp=int64#1,<pc=int64#2,8),>spc=int64#6
+# asm 2: movq (<hp=%rdi,<pc=%rsi,8),>spc=%r9
+movq (%rdi,%rsi,8),%r9
+
+# qhasm: spp <<= 5
+# asm 1: shl $5,<spp=int64#5
+# asm 2: shl $5,<spp=%r8
+shl $5,%r8
+
+# qhasm: spc <<= 5
+# asm 1: shl $5,<spc=int64#6
+# asm 2: shl $5,<spc=%r9
+shl $5,%r9
+
+# qhasm: spc += sp
+# asm 1: add <sp=int64#3,<spc=int64#6
+# asm 2: add <sp=%rdx,<spc=%r9
+add %rdx,%r9
+
+# qhasm: spp += sp
+# asm 1: add <sp=int64#3,<spp=int64#5
+# asm 2: add <sp=%rdx,<spp=%r8
+add %rdx,%r8
+
+# qhasm: c0 = *(uint64 *)(spc + 0)
+# asm 1: movq 0(<spc=int64#6),>c0=int64#7
+# asm 2: movq 0(<spc=%r9),>c0=%rax
+movq 0(%r9),%rax
+
+# qhasm: c1 = *(uint64 *)(spc + 8)
+# asm 1: movq 8(<spc=int64#6),>c1=int64#8
+# asm 2: movq 8(<spc=%r9),>c1=%r10
+movq 8(%r9),%r10
+
+# qhasm: carry? c0 -= *(uint64 *)(spp + 0)
+# asm 1: subq 0(<spp=int64#5),<c0=int64#7
+# asm 2: subq 0(<spp=%r8),<c0=%rax
+subq 0(%r8),%rax
+
+# qhasm: carry? c1 -= *(uint64 *)(spp + 8) - carry
+# asm 1: sbbq 8(<spp=int64#5),<c1=int64#8
+# asm 2: sbbq 8(<spp=%r8),<c1=%r10
+sbbq 8(%r8),%r10
+# comment:fp stack unchanged by jump
+
+# qhasm: goto end if carry
+jc ._end
+
+# qhasm: spc -= sp
+# asm 1: sub <sp=int64#3,<spc=int64#6
+# asm 2: sub <sp=%rdx,<spc=%r9
+sub %rdx,%r9
+
+# qhasm: (uint64) spc >>= 5
+# asm 1: shr $5,<spc=int64#6
+# asm 2: shr $5,<spc=%r9
+shr $5,%r9
+
+# qhasm: spp -= sp
+# asm 1: sub <sp=int64#3,<spp=int64#5
+# asm 2: sub <sp=%rdx,<spp=%r8
+sub %rdx,%r8
+
+# qhasm: (uint64) spp >>= 5
+# asm 1: shr $5,<spp=int64#5
+# asm 2: shr $5,<spp=%r8
+shr $5,%r8
+
+# qhasm: *(uint64 *)(hp + pp * 8) = spc
+# asm 1: movq <spc=int64#6,(<hp=int64#1,<pp=int64#4,8)
+# asm 2: movq <spc=%r9,(<hp=%rdi,<pp=%rcx,8)
+movq %r9,(%rdi,%rcx,8)
+
+# qhasm: *(uint64 *)(hp + pc * 8) = spp
+# asm 1: movq <spp=int64#5,(<hp=int64#1,<pc=int64#2,8)
+# asm 2: movq <spp=%r8,(<hp=%rdi,<pc=%rsi,8)
+movq %r8,(%rdi,%rsi,8)
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftuploop
+jmp ._siftuploop
+
+# qhasm: end:
+._end:
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/heap_rootreplaced_3limbs.s b/ext/ed25519-amd64-asm/heap_rootreplaced_3limbs.s
new file mode 100644
index 00000000..dcf890ea
--- /dev/null
+++ b/ext/ed25519-amd64-asm/heap_rootreplaced_3limbs.s
@@ -0,0 +1,456 @@
+
+# qhasm: int64 hp
+
+# qhasm: int64 hlen
+
+# qhasm: int64 sp
+
+# qhasm: int64 pp
+
+# qhasm: input hp
+
+# qhasm: input hlen
+
+# qhasm: input sp
+
+# qhasm: int64 prc
+
+# qhasm: int64 plc
+
+# qhasm: int64 pc
+
+# qhasm: int64 d
+
+# qhasm: int64 spp
+
+# qhasm: int64 sprc
+
+# qhasm: int64 spc
+
+# qhasm: int64 c0
+
+# qhasm: int64 c1
+
+# qhasm: int64 c2
+
+# qhasm: int64 c3
+
+# qhasm: int64 t0
+
+# qhasm: int64 t1
+
+# qhasm: int64 t2
+
+# qhasm: int64 t3
+
+# qhasm: int64 p0
+
+# qhasm: int64 p1
+
+# qhasm: int64 p2
+
+# qhasm: int64 p3
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_heap_rootreplaced_3limbs
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_heap_rootreplaced_3limbs
+.globl crypto_sign_ed25519_amd64_64_heap_rootreplaced_3limbs
+_crypto_sign_ed25519_amd64_64_heap_rootreplaced_3limbs:
+crypto_sign_ed25519_amd64_64_heap_rootreplaced_3limbs:
+mov %rsp,%r11
+and $31,%r11
+add $64,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: pp = 0
+# asm 1: mov $0,>pp=int64#4
+# asm 2: mov $0,>pp=%rcx
+mov $0,%rcx
+
+# qhasm: siftdownloop:
+._siftdownloop:
+
+# qhasm: prc = pp
+# asm 1: mov <pp=int64#4,>prc=int64#5
+# asm 2: mov <pp=%rcx,>prc=%r8
+mov %rcx,%r8
+
+# qhasm: prc *= 2
+# asm 1: imulq $2,<prc=int64#5,>prc=int64#5
+# asm 2: imulq $2,<prc=%r8,>prc=%r8
+imulq $2,%r8,%r8
+
+# qhasm: pc = prc
+# asm 1: mov <prc=int64#5,>pc=int64#6
+# asm 2: mov <prc=%r8,>pc=%r9
+mov %r8,%r9
+
+# qhasm: prc += 2
+# asm 1: add $2,<prc=int64#5
+# asm 2: add $2,<prc=%r8
+add $2,%r8
+
+# qhasm: pc += 1
+# asm 1: add $1,<pc=int64#6
+# asm 2: add $1,<pc=%r9
+add $1,%r9
+
+# qhasm: unsigned>? hlen - prc
+# asm 1: cmp <prc=int64#5,<hlen=int64#2
+# asm 2: cmp <prc=%r8,<hlen=%rsi
+cmp %r8,%rsi
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftuploop if !unsigned>
+jbe ._siftuploop
+
+# qhasm: sprc = *(uint64 *)(hp + prc * 8)
+# asm 1: movq (<hp=int64#1,<prc=int64#5,8),>sprc=int64#7
+# asm 2: movq (<hp=%rdi,<prc=%r8,8),>sprc=%rax
+movq (%rdi,%r8,8),%rax
+
+# qhasm: sprc <<= 5
+# asm 1: shl $5,<sprc=int64#7
+# asm 2: shl $5,<sprc=%rax
+shl $5,%rax
+
+# qhasm: sprc += sp
+# asm 1: add <sp=int64#3,<sprc=int64#7
+# asm 2: add <sp=%rdx,<sprc=%rax
+add %rdx,%rax
+
+# qhasm: spc = *(uint64 *)(hp + pc * 8)
+# asm 1: movq (<hp=int64#1,<pc=int64#6,8),>spc=int64#8
+# asm 2: movq (<hp=%rdi,<pc=%r9,8),>spc=%r10
+movq (%rdi,%r9,8),%r10
+
+# qhasm: spc <<= 5
+# asm 1: shl $5,<spc=int64#8
+# asm 2: shl $5,<spc=%r10
+shl $5,%r10
+
+# qhasm: spc += sp
+# asm 1: add <sp=int64#3,<spc=int64#8
+# asm 2: add <sp=%rdx,<spc=%r10
+add %rdx,%r10
+
+# qhasm: c0 = *(uint64 *)(spc + 0)
+# asm 1: movq 0(<spc=int64#8),>c0=int64#9
+# asm 2: movq 0(<spc=%r10),>c0=%r11
+movq 0(%r10),%r11
+
+# qhasm: c1 = *(uint64 *)(spc + 8)
+# asm 1: movq 8(<spc=int64#8),>c1=int64#10
+# asm 2: movq 8(<spc=%r10),>c1=%r12
+movq 8(%r10),%r12
+
+# qhasm: c2 = *(uint64 *)(spc + 16)
+# asm 1: movq 16(<spc=int64#8),>c2=int64#11
+# asm 2: movq 16(<spc=%r10),>c2=%r13
+movq 16(%r10),%r13
+
+# qhasm: carry? c0 -= *(uint64 *)(sprc + 0)
+# asm 1: subq 0(<sprc=int64#7),<c0=int64#9
+# asm 2: subq 0(<sprc=%rax),<c0=%r11
+subq 0(%rax),%r11
+
+# qhasm: carry? c1 -= *(uint64 *)(sprc + 8) - carry
+# asm 1: sbbq 8(<sprc=int64#7),<c1=int64#10
+# asm 2: sbbq 8(<sprc=%rax),<c1=%r12
+sbbq 8(%rax),%r12
+
+# qhasm: carry? c2 -= *(uint64 *)(sprc + 16) - carry
+# asm 1: sbbq 16(<sprc=int64#7),<c2=int64#11
+# asm 2: sbbq 16(<sprc=%rax),<c2=%r13
+sbbq 16(%rax),%r13
+
+# qhasm: pc = prc if carry
+# asm 1: cmovc <prc=int64#5,<pc=int64#6
+# asm 2: cmovc <prc=%r8,<pc=%r9
+cmovc %r8,%r9
+
+# qhasm: spc = sprc if carry
+# asm 1: cmovc <sprc=int64#7,<spc=int64#8
+# asm 2: cmovc <sprc=%rax,<spc=%r10
+cmovc %rax,%r10
+
+# qhasm: spc -= sp
+# asm 1: sub <sp=int64#3,<spc=int64#8
+# asm 2: sub <sp=%rdx,<spc=%r10
+sub %rdx,%r10
+
+# qhasm: (uint64) spc >>= 5
+# asm 1: shr $5,<spc=int64#8
+# asm 2: shr $5,<spc=%r10
+shr $5,%r10
+
+# qhasm: spp = *(uint64 *)(hp + pp * 8)
+# asm 1: movq (<hp=int64#1,<pp=int64#4,8),>spp=int64#5
+# asm 2: movq (<hp=%rdi,<pp=%rcx,8),>spp=%r8
+movq (%rdi,%rcx,8),%r8
+
+# qhasm: *(uint64 *)(hp + pp * 8) = spc
+# asm 1: movq <spc=int64#8,(<hp=int64#1,<pp=int64#4,8)
+# asm 2: movq <spc=%r10,(<hp=%rdi,<pp=%rcx,8)
+movq %r10,(%rdi,%rcx,8)
+
+# qhasm: *(uint64 *)(hp + pc * 8) = spp
+# asm 1: movq <spp=int64#5,(<hp=int64#1,<pc=int64#6,8)
+# asm 2: movq <spp=%r8,(<hp=%rdi,<pc=%r9,8)
+movq %r8,(%rdi,%r9,8)
+
+# qhasm: pp = pc
+# asm 1: mov <pc=int64#6,>pp=int64#4
+# asm 2: mov <pc=%r9,>pp=%rcx
+mov %r9,%rcx
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftdownloop
+jmp ._siftdownloop
+
+# qhasm: siftuploop:
+._siftuploop:
+
+# qhasm: pc = pp
+# asm 1: mov <pp=int64#4,>pc=int64#2
+# asm 2: mov <pp=%rcx,>pc=%rsi
+mov %rcx,%rsi
+
+# qhasm: pp -= 1
+# asm 1: sub $1,<pp=int64#4
+# asm 2: sub $1,<pp=%rcx
+sub $1,%rcx
+
+# qhasm: (uint64) pp >>= 1
+# asm 1: shr $1,<pp=int64#4
+# asm 2: shr $1,<pp=%rcx
+shr $1,%rcx
+
+# qhasm: unsigned>? pc - 0
+# asm 1: cmp $0,<pc=int64#2
+# asm 2: cmp $0,<pc=%rsi
+cmp $0,%rsi
+# comment:fp stack unchanged by jump
+
+# qhasm: goto end if !unsigned>
+jbe ._end
+
+# qhasm: spp = *(uint64 *)(hp + pp * 8)
+# asm 1: movq (<hp=int64#1,<pp=int64#4,8),>spp=int64#5
+# asm 2: movq (<hp=%rdi,<pp=%rcx,8),>spp=%r8
+movq (%rdi,%rcx,8),%r8
+
+# qhasm: spc = *(uint64 *)(hp + pc * 8)
+# asm 1: movq (<hp=int64#1,<pc=int64#2,8),>spc=int64#6
+# asm 2: movq (<hp=%rdi,<pc=%rsi,8),>spc=%r9
+movq (%rdi,%rsi,8),%r9
+
+# qhasm: spp <<= 5
+# asm 1: shl $5,<spp=int64#5
+# asm 2: shl $5,<spp=%r8
+shl $5,%r8
+
+# qhasm: spc <<= 5
+# asm 1: shl $5,<spc=int64#6
+# asm 2: shl $5,<spc=%r9
+shl $5,%r9
+
+# qhasm: spc += sp
+# asm 1: add <sp=int64#3,<spc=int64#6
+# asm 2: add <sp=%rdx,<spc=%r9
+add %rdx,%r9
+
+# qhasm: spp += sp
+# asm 1: add <sp=int64#3,<spp=int64#5
+# asm 2: add <sp=%rdx,<spp=%r8
+add %rdx,%r8
+
+# qhasm: c0 = *(uint64 *)(spc + 0)
+# asm 1: movq 0(<spc=int64#6),>c0=int64#7
+# asm 2: movq 0(<spc=%r9),>c0=%rax
+movq 0(%r9),%rax
+
+# qhasm: c1 = *(uint64 *)(spc + 8)
+# asm 1: movq 8(<spc=int64#6),>c1=int64#8
+# asm 2: movq 8(<spc=%r9),>c1=%r10
+movq 8(%r9),%r10
+
+# qhasm: c2 = *(uint64 *)(spc + 16)
+# asm 1: movq 16(<spc=int64#6),>c2=int64#9
+# asm 2: movq 16(<spc=%r9),>c2=%r11
+movq 16(%r9),%r11
+
+# qhasm: carry? c0 -= *(uint64 *)(spp + 0)
+# asm 1: subq 0(<spp=int64#5),<c0=int64#7
+# asm 2: subq 0(<spp=%r8),<c0=%rax
+subq 0(%r8),%rax
+
+# qhasm: carry? c1 -= *(uint64 *)(spp + 8) - carry
+# asm 1: sbbq 8(<spp=int64#5),<c1=int64#8
+# asm 2: sbbq 8(<spp=%r8),<c1=%r10
+sbbq 8(%r8),%r10
+
+# qhasm: carry? c2 -= *(uint64 *)(spp + 16) - carry
+# asm 1: sbbq 16(<spp=int64#5),<c2=int64#9
+# asm 2: sbbq 16(<spp=%r8),<c2=%r11
+sbbq 16(%r8),%r11
+# comment:fp stack unchanged by jump
+
+# qhasm: goto end if carry
+jc ._end
+
+# qhasm: spc -= sp
+# asm 1: sub <sp=int64#3,<spc=int64#6
+# asm 2: sub <sp=%rdx,<spc=%r9
+sub %rdx,%r9
+
+# qhasm: (uint64) spc >>= 5
+# asm 1: shr $5,<spc=int64#6
+# asm 2: shr $5,<spc=%r9
+shr $5,%r9
+
+# qhasm: spp -= sp
+# asm 1: sub <sp=int64#3,<spp=int64#5
+# asm 2: sub <sp=%rdx,<spp=%r8
+sub %rdx,%r8
+
+# qhasm: (uint64) spp >>= 5
+# asm 1: shr $5,<spp=int64#5
+# asm 2: shr $5,<spp=%r8
+shr $5,%r8
+
+# qhasm: *(uint64 *)(hp + pp * 8) = spc
+# asm 1: movq <spc=int64#6,(<hp=int64#1,<pp=int64#4,8)
+# asm 2: movq <spc=%r9,(<hp=%rdi,<pp=%rcx,8)
+movq %r9,(%rdi,%rcx,8)
+
+# qhasm: *(uint64 *)(hp + pc * 8) = spp
+# asm 1: movq <spp=int64#5,(<hp=int64#1,<pc=int64#2,8)
+# asm 2: movq <spp=%r8,(<hp=%rdi,<pc=%rsi,8)
+movq %r8,(%rdi,%rsi,8)
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftuploop
+jmp ._siftuploop
+
+# qhasm: end:
+._end:
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/hram.c b/ext/ed25519-amd64-asm/hram.c
new file mode 100644
index 00000000..6f99fc62
--- /dev/null
+++ b/ext/ed25519-amd64-asm/hram.c
@@ -0,0 +1,16 @@
+/*#include "crypto_hash_sha512.h"*/
+#include "hram.h"
+
+extern void ZT_sha512internal(void *digest,const void *data,unsigned int len);
+
+void get_hram(unsigned char *hram, const unsigned char *sm, const unsigned char *pk, unsigned char *playground, unsigned long long smlen)
+{
+ unsigned long long i;
+
+ for (i = 0;i < 32;++i) playground[i] = sm[i];
+ for (i = 32;i < 64;++i) playground[i] = pk[i-32];
+ for (i = 64;i < smlen;++i) playground[i] = sm[i];
+
+ /*crypto_hash_sha512(hram,playground,smlen);*/
+ ZT_sha512internal(hram,playground,smlen);
+}
diff --git a/ext/ed25519-amd64-asm/hram.h b/ext/ed25519-amd64-asm/hram.h
new file mode 100644
index 00000000..1740c78a
--- /dev/null
+++ b/ext/ed25519-amd64-asm/hram.h
@@ -0,0 +1,8 @@
+#ifndef HRAM_H
+#define HRAM_H
+
+#define get_hram crypto_sign_ed25519_amd64_64_get_hram
+
+extern void get_hram(unsigned char *hram, const unsigned char *sm, const unsigned char *pk, unsigned char *playground, unsigned long long smlen);
+
+#endif
diff --git a/ext/ed25519-amd64-asm/implementors b/ext/ed25519-amd64-asm/implementors
new file mode 100644
index 00000000..9b5399a3
--- /dev/null
+++ b/ext/ed25519-amd64-asm/implementors
@@ -0,0 +1,5 @@
+Daniel J. Bernstein
+Niels Duif
+Tanja Lange
+lead: Peter Schwabe
+Bo-Yin Yang
diff --git a/ext/ed25519-amd64-asm/index_heap.c b/ext/ed25519-amd64-asm/index_heap.c
new file mode 100644
index 00000000..f29f7a28
--- /dev/null
+++ b/ext/ed25519-amd64-asm/index_heap.c
@@ -0,0 +1,58 @@
+#include "sc25519.h"
+#include "index_heap.h"
+
+/* caller's responsibility to ensure hlen>=3 */
+void heap_init(unsigned long long *h, unsigned long long hlen, sc25519 *scalars)
+{
+ h[0] = 0;
+ unsigned long long i=1;
+ while(i<hlen)
+ heap_push(h, &i, i, scalars);
+}
+
+void heap_extend(unsigned long long *h, unsigned long long oldlen, unsigned long long newlen, sc25519 *scalars)
+{
+ unsigned long long i=oldlen;
+ while(i<newlen)
+ heap_push(h, &i, i, scalars);
+}
+
+
+void heap_push(unsigned long long *h, unsigned long long *hlen, unsigned long long elem, sc25519 *scalars)
+{
+ /* Move up towards the root */
+ /* XXX: Check size of hlen, whether cast to signed value is ok */
+ signed long long pos = *hlen;
+ signed long long ppos = (pos-1)/2;
+ unsigned long long t;
+ h[*hlen] = elem;
+ while(pos > 0)
+ {
+ /* if(sc25519_lt_vartime(&scalars[h[ppos]], &scalars[h[pos]])) */
+ if(sc25519_lt(&scalars[h[ppos]], &scalars[h[pos]]))
+ {
+ t = h[ppos];
+ h[ppos] = h[pos];
+ h[pos] = t;
+ pos = ppos;
+ ppos = (pos-1)/2;
+ }
+ else break;
+ }
+ (*hlen)++;
+}
+
+/* Put the largest value in the heap in max1, the second largest in max2 */
+void heap_get2max(unsigned long long *h, unsigned long long *max1, unsigned long long *max2, sc25519 *scalars)
+{
+ *max1 = h[0];
+ *max2 = h[1];
+ if(sc25519_lt(&scalars[h[1]],&scalars[h[2]]))
+ *max2 = h[2];
+}
+
+/* After the root has been replaced, restore heap property */
+/* extern void heap_rootreplaced(unsigned long long *h, unsigned long long hlen, sc25519 *scalars);
+*/
+/* extern void heap_rootreplaced_shortscalars(unsigned long long *h, unsigned long long hlen, sc25519 *scalars);
+*/
diff --git a/ext/ed25519-amd64-asm/index_heap.h b/ext/ed25519-amd64-asm/index_heap.h
new file mode 100644
index 00000000..7dee9161
--- /dev/null
+++ b/ext/ed25519-amd64-asm/index_heap.h
@@ -0,0 +1,31 @@
+#ifndef INDEX_HEAP_H
+#define INDEX_HEAP_H
+
+#include "sc25519.h"
+
+#define heap_init crypto_sign_ed25519_amd64_64_heap_init
+#define heap_extend crypto_sign_ed25519_amd64_64_heap_extend
+#define heap_pop crypto_sign_ed25519_amd64_64_heap_pop
+#define heap_push crypto_sign_ed25519_amd64_64_heap_push
+#define heap_get2max crypto_sign_ed25519_amd64_64_heap_get2max
+#define heap_rootreplaced crypto_sign_ed25519_amd64_64_heap_rootreplaced
+#define heap_rootreplaced_3limbs crypto_sign_ed25519_amd64_64_heap_rootreplaced_3limbs
+#define heap_rootreplaced_2limbs crypto_sign_ed25519_amd64_64_heap_rootreplaced_2limbs
+#define heap_rootreplaced_1limb crypto_sign_ed25519_amd64_64_heap_rootreplaced_1limb
+
+void heap_init(unsigned long long *h, unsigned long long hlen, sc25519 *scalars);
+
+void heap_extend(unsigned long long *h, unsigned long long oldlen, unsigned long long newlen, sc25519 *scalars);
+
+unsigned long long heap_pop(unsigned long long *h, unsigned long long *hlen, sc25519 *scalars);
+
+void heap_push(unsigned long long *h, unsigned long long *hlen, unsigned long long elem, sc25519 *scalars);
+
+void heap_get2max(unsigned long long *h, unsigned long long *max1, unsigned long long *max2, sc25519 *scalars);
+
+void heap_rootreplaced(unsigned long long *h, unsigned long long hlen, sc25519 *scalars);
+void heap_rootreplaced_3limbs(unsigned long long *h, unsigned long long hlen, sc25519 *scalars);
+void heap_rootreplaced_2limbs(unsigned long long *h, unsigned long long hlen, sc25519 *scalars);
+void heap_rootreplaced_1limb(unsigned long long *h, unsigned long long hlen, sc25519 *scalars);
+
+#endif
diff --git a/ext/ed25519-amd64-asm/keypair.c b/ext/ed25519-amd64-asm/keypair.c
new file mode 100644
index 00000000..7e094710
--- /dev/null
+++ b/ext/ed25519-amd64-asm/keypair.c
@@ -0,0 +1,25 @@
+#include <string.h>
+#include "crypto_sign.h"
+#include "crypto_hash_sha512.h"
+#include "randombytes.h"
+#include "ge25519.h"
+
+int crypto_sign_keypair(unsigned char *pk,unsigned char *sk)
+{
+ unsigned char az[64];
+ sc25519 scsk;
+ ge25519 gepk;
+
+ randombytes(sk,32);
+ crypto_hash_sha512(az,sk,32);
+ az[0] &= 248;
+ az[31] &= 127;
+ az[31] |= 64;
+
+ sc25519_from32bytes(&scsk,az);
+
+ ge25519_scalarmult_base(&gepk, &scsk);
+ ge25519_pack(pk, &gepk);
+ memmove(sk + 32,pk,32);
+ return 0;
+}
diff --git a/ext/ed25519-amd64-asm/open.c b/ext/ed25519-amd64-asm/open.c
new file mode 100644
index 00000000..104d48dc
--- /dev/null
+++ b/ext/ed25519-amd64-asm/open.c
@@ -0,0 +1,49 @@
+#include <string.h>
+#include "crypto_sign.h"
+#include "crypto_verify_32.h"
+#include "crypto_hash_sha512.h"
+#include "ge25519.h"
+
+int crypto_sign_open(
+ unsigned char *m,unsigned long long *mlen,
+ const unsigned char *sm,unsigned long long smlen,
+ const unsigned char *pk
+ )
+{
+ unsigned char pkcopy[32];
+ unsigned char rcopy[32];
+ unsigned char hram[64];
+ unsigned char rcheck[32];
+ ge25519 get1, get2;
+ sc25519 schram, scs;
+
+ if (smlen < 64) goto badsig;
+ if (sm[63] & 224) goto badsig;
+ if (ge25519_unpackneg_vartime(&get1,pk)) goto badsig;
+
+ memmove(pkcopy,pk,32);
+ memmove(rcopy,sm,32);
+
+ sc25519_from32bytes(&scs, sm+32);
+
+ memmove(m,sm,smlen);
+ memmove(m + 32,pkcopy,32);
+ crypto_hash_sha512(hram,m,smlen);
+
+ sc25519_from64bytes(&schram, hram);
+
+ ge25519_double_scalarmult_vartime(&get2, &get1, &schram, &scs);
+ ge25519_pack(rcheck, &get2);
+
+ if (crypto_verify_32(rcopy,rcheck) == 0) {
+ memmove(m,m + 64,smlen - 64);
+ memset(m + smlen - 64,0,64);
+ *mlen = smlen - 64;
+ return 0;
+ }
+
+badsig:
+ *mlen = (unsigned long long) -1;
+ memset(m,0,smlen);
+ return -1;
+}
diff --git a/ext/ed25519-amd64-asm/sc25519.h b/ext/ed25519-amd64-asm/sc25519.h
new file mode 100644
index 00000000..8ff1b1ca
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519.h
@@ -0,0 +1,66 @@
+#ifndef SC25519_H
+#define SC25519_H
+
+#define sc25519 crypto_sign_ed25519_amd64_64_sc25519
+#define shortsc25519 crypto_sign_ed25519_amd64_64_shortsc25519
+#define sc25519_from32bytes crypto_sign_ed25519_amd64_64_sc25519_from32bytes
+#define shortsc25519_from16bytes crypto_sign_ed25519_amd64_64_shortsc25519_from16bytes
+#define sc25519_from64bytes crypto_sign_ed25519_amd64_64_sc25519_from64bytes
+#define sc25519_from_shortsc crypto_sign_ed25519_amd64_64_sc25519_from_shortsc
+#define sc25519_to32bytes crypto_sign_ed25519_amd64_64_sc25519_to32bytes
+#define sc25519_iszero_vartime crypto_sign_ed25519_amd64_64_sc25519_iszero_vartime
+#define sc25519_isshort_vartime crypto_sign_ed25519_amd64_64_sc25519_isshort_vartime
+#define sc25519_lt crypto_sign_ed25519_amd64_64_sc25519_lt
+#define sc25519_add crypto_sign_ed25519_amd64_64_sc25519_add
+#define sc25519_sub_nored crypto_sign_ed25519_amd64_64_sc25519_sub_nored
+#define sc25519_mul crypto_sign_ed25519_amd64_64_sc25519_mul
+#define sc25519_mul_shortsc crypto_sign_ed25519_amd64_64_sc25519_mul_shortsc
+#define sc25519_window4 crypto_sign_ed25519_amd64_64_sc25519_window4
+#define sc25519_slide crypto_sign_ed25519_amd64_64_sc25519_slide
+#define sc25519_2interleave2 crypto_sign_ed25519_amd64_64_sc25519_2interleave2
+#define sc25519_barrett crypto_sign_ed25519_amd64_64_sc25519_barrett
+
+typedef struct
+{
+ unsigned long long v[4];
+}
+sc25519;
+
+typedef struct
+{
+ unsigned long long v[2];
+}
+shortsc25519;
+
+void sc25519_from32bytes(sc25519 *r, const unsigned char x[32]);
+
+void sc25519_from64bytes(sc25519 *r, const unsigned char x[64]);
+
+void sc25519_from_shortsc(sc25519 *r, const shortsc25519 *x);
+
+void sc25519_to32bytes(unsigned char r[32], const sc25519 *x);
+
+int sc25519_iszero_vartime(const sc25519 *x);
+
+int sc25519_lt(const sc25519 *x, const sc25519 *y);
+
+void sc25519_add(sc25519 *r, const sc25519 *x, const sc25519 *y);
+
+void sc25519_sub_nored(sc25519 *r, const sc25519 *x, const sc25519 *y);
+
+void sc25519_mul(sc25519 *r, const sc25519 *x, const sc25519 *y);
+
+void sc25519_mul_shortsc(sc25519 *r, const sc25519 *x, const shortsc25519 *y);
+
+/* Convert s into a representation of the form \sum_{i=0}^{63}r[i]2^(4*i)
+ * with r[i] in {-8,...,7}
+ */
+void sc25519_window4(signed char r[85], const sc25519 *s);
+
+void sc25519_slide(signed char r[256], const sc25519 *s, int swindowsize);
+
+void sc25519_2interleave2(unsigned char r[127], const sc25519 *s1, const sc25519 *s2);
+
+void sc25519_barrett(sc25519 *r, unsigned long long x[8]);
+
+#endif
diff --git a/ext/ed25519-amd64-asm/sc25519_add.s b/ext/ed25519-amd64-asm/sc25519_add.s
new file mode 100644
index 00000000..71de0024
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_add.s
@@ -0,0 +1,232 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 xp
+
+# qhasm: int64 yp
+
+# qhasm: input rp
+
+# qhasm: input xp
+
+# qhasm: input yp
+
+# qhasm: int64 r0
+
+# qhasm: int64 r1
+
+# qhasm: int64 r2
+
+# qhasm: int64 r3
+
+# qhasm: int64 t0
+
+# qhasm: int64 t1
+
+# qhasm: int64 t2
+
+# qhasm: int64 t3
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_sc25519_add
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_sc25519_add
+.globl crypto_sign_ed25519_amd64_64_sc25519_add
+_crypto_sign_ed25519_amd64_64_sc25519_add:
+crypto_sign_ed25519_amd64_64_sc25519_add:
+mov %rsp,%r11
+and $31,%r11
+add $32,%r11
+sub %r11,%rsp
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#1
+# asm 2: movq <caller4=%r14,>caller4_stack=0(%rsp)
+movq %r14,0(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#2
+# asm 2: movq <caller5=%r15,>caller5_stack=8(%rsp)
+movq %r15,8(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#3
+# asm 2: movq <caller6=%rbx,>caller6_stack=16(%rsp)
+movq %rbx,16(%rsp)
+
+# qhasm: r0 = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#2),>r0=int64#4
+# asm 2: movq 0(<xp=%rsi),>r0=%rcx
+movq 0(%rsi),%rcx
+
+# qhasm: r1 = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>r1=int64#5
+# asm 2: movq 8(<xp=%rsi),>r1=%r8
+movq 8(%rsi),%r8
+
+# qhasm: r2 = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>r2=int64#6
+# asm 2: movq 16(<xp=%rsi),>r2=%r9
+movq 16(%rsi),%r9
+
+# qhasm: r3 = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>r3=int64#2
+# asm 2: movq 24(<xp=%rsi),>r3=%rsi
+movq 24(%rsi),%rsi
+
+# qhasm: carry? r0 += *(uint64 *)(yp + 0)
+# asm 1: addq 0(<yp=int64#3),<r0=int64#4
+# asm 2: addq 0(<yp=%rdx),<r0=%rcx
+addq 0(%rdx),%rcx
+
+# qhasm: carry? r1 += *(uint64 *)(yp + 8) + carry
+# asm 1: adcq 8(<yp=int64#3),<r1=int64#5
+# asm 2: adcq 8(<yp=%rdx),<r1=%r8
+adcq 8(%rdx),%r8
+
+# qhasm: carry? r2 += *(uint64 *)(yp + 16) + carry
+# asm 1: adcq 16(<yp=int64#3),<r2=int64#6
+# asm 2: adcq 16(<yp=%rdx),<r2=%r9
+adcq 16(%rdx),%r9
+
+# qhasm: r3 += *(uint64 *)(yp + 24) + carry
+# asm 1: adcq 24(<yp=int64#3),<r3=int64#2
+# asm 2: adcq 24(<yp=%rdx),<r3=%rsi
+adcq 24(%rdx),%rsi
+
+# qhasm: t0 = r0
+# asm 1: mov <r0=int64#4,>t0=int64#3
+# asm 2: mov <r0=%rcx,>t0=%rdx
+mov %rcx,%rdx
+
+# qhasm: t1 = r1
+# asm 1: mov <r1=int64#5,>t1=int64#7
+# asm 2: mov <r1=%r8,>t1=%rax
+mov %r8,%rax
+
+# qhasm: t2 = r2
+# asm 1: mov <r2=int64#6,>t2=int64#8
+# asm 2: mov <r2=%r9,>t2=%r10
+mov %r9,%r10
+
+# qhasm: t3 = r3
+# asm 1: mov <r3=int64#2,>t3=int64#12
+# asm 2: mov <r3=%rsi,>t3=%r14
+mov %rsi,%r14
+
+# qhasm: carry? t0 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER0
+# asm 1: sub crypto_sign_ed25519_amd64_64_ORDER0,<t0=int64#3
+# asm 2: sub crypto_sign_ed25519_amd64_64_ORDER0,<t0=%rdx
+sub crypto_sign_ed25519_amd64_64_ORDER0,%rdx
+
+# qhasm: carry? t1 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER1 - carry
+# asm 1: sbb crypto_sign_ed25519_amd64_64_ORDER1,<t1=int64#7
+# asm 2: sbb crypto_sign_ed25519_amd64_64_ORDER1,<t1=%rax
+sbb crypto_sign_ed25519_amd64_64_ORDER1,%rax
+
+# qhasm: carry? t2 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER2 - carry
+# asm 1: sbb crypto_sign_ed25519_amd64_64_ORDER2,<t2=int64#8
+# asm 2: sbb crypto_sign_ed25519_amd64_64_ORDER2,<t2=%r10
+sbb crypto_sign_ed25519_amd64_64_ORDER2,%r10
+
+# qhasm: unsigned<? t3 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER3 - carry
+# asm 1: sbb crypto_sign_ed25519_amd64_64_ORDER3,<t3=int64#12
+# asm 2: sbb crypto_sign_ed25519_amd64_64_ORDER3,<t3=%r14
+sbb crypto_sign_ed25519_amd64_64_ORDER3,%r14
+
+# qhasm: r0 = t0 if !unsigned<
+# asm 1: cmovae <t0=int64#3,<r0=int64#4
+# asm 2: cmovae <t0=%rdx,<r0=%rcx
+cmovae %rdx,%rcx
+
+# qhasm: r1 = t1 if !unsigned<
+# asm 1: cmovae <t1=int64#7,<r1=int64#5
+# asm 2: cmovae <t1=%rax,<r1=%r8
+cmovae %rax,%r8
+
+# qhasm: r2 = t2 if !unsigned<
+# asm 1: cmovae <t2=int64#8,<r2=int64#6
+# asm 2: cmovae <t2=%r10,<r2=%r9
+cmovae %r10,%r9
+
+# qhasm: r3 = t3 if !unsigned<
+# asm 1: cmovae <t3=int64#12,<r3=int64#2
+# asm 2: cmovae <t3=%r14,<r3=%rsi
+cmovae %r14,%rsi
+
+# qhasm: *(uint64 *)(rp + 0) = r0
+# asm 1: movq <r0=int64#4,0(<rp=int64#1)
+# asm 2: movq <r0=%rcx,0(<rp=%rdi)
+movq %rcx,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = r1
+# asm 1: movq <r1=int64#5,8(<rp=int64#1)
+# asm 2: movq <r1=%r8,8(<rp=%rdi)
+movq %r8,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = r2
+# asm 1: movq <r2=int64#6,16(<rp=int64#1)
+# asm 2: movq <r2=%r9,16(<rp=%rdi)
+movq %r9,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = r3
+# asm 1: movq <r3=int64#2,24(<rp=int64#1)
+# asm 2: movq <r3=%rsi,24(<rp=%rdi)
+movq %rsi,24(%rdi)
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#1,>caller4=int64#12
+# asm 2: movq <caller4_stack=0(%rsp),>caller4=%r14
+movq 0(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#2,>caller5=int64#13
+# asm 2: movq <caller5_stack=8(%rsp),>caller5=%r15
+movq 8(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#3,>caller6=int64#14
+# asm 2: movq <caller6_stack=16(%rsp),>caller6=%rbx
+movq 16(%rsp),%rbx
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/sc25519_barrett.s b/ext/ed25519-amd64-asm/sc25519_barrett.s
new file mode 100644
index 00000000..c59f4563
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_barrett.s
@@ -0,0 +1,1188 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 xp
+
+# qhasm: input rp
+
+# qhasm: input xp
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: int64 q23
+
+# qhasm: int64 q24
+
+# qhasm: int64 q30
+
+# qhasm: int64 q31
+
+# qhasm: int64 q32
+
+# qhasm: int64 q33
+
+# qhasm: int64 r20
+
+# qhasm: int64 r21
+
+# qhasm: int64 r22
+
+# qhasm: int64 r23
+
+# qhasm: int64 r24
+
+# qhasm: int64 r0
+
+# qhasm: int64 r1
+
+# qhasm: int64 r2
+
+# qhasm: int64 r3
+
+# qhasm: int64 t0
+
+# qhasm: int64 t1
+
+# qhasm: int64 t2
+
+# qhasm: int64 t3
+
+# qhasm: int64 rax
+
+# qhasm: int64 rdx
+
+# qhasm: int64 c
+
+# qhasm: int64 zero
+
+# qhasm: int64 mask
+
+# qhasm: int64 nmask
+
+# qhasm: stack64 q30_stack
+
+# qhasm: stack64 q31_stack
+
+# qhasm: stack64 q32_stack
+
+# qhasm: stack64 q33_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_sc25519_barrett
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_sc25519_barrett
+.globl crypto_sign_ed25519_amd64_64_sc25519_barrett
+_crypto_sign_ed25519_amd64_64_sc25519_barrett:
+crypto_sign_ed25519_amd64_64_sc25519_barrett:
+mov %rsp,%r11
+and $31,%r11
+add $96,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: zero ^= zero
+# asm 1: xor <zero=int64#4,<zero=int64#4
+# asm 2: xor <zero=%rcx,<zero=%rcx
+xor %rcx,%rcx
+
+# qhasm: q30 ^= q30
+# asm 1: xor <q30=int64#5,<q30=int64#5
+# asm 2: xor <q30=%r8,<q30=%r8
+xor %r8,%r8
+
+# qhasm: q31 ^= q31
+# asm 1: xor <q31=int64#6,<q31=int64#6
+# asm 2: xor <q31=%r9,<q31=%r9
+xor %r9,%r9
+
+# qhasm: q32 ^= q32
+# asm 1: xor <q32=int64#8,<q32=int64#8
+# asm 2: xor <q32=%r10,<q32=%r10
+xor %r10,%r10
+
+# qhasm: q33 ^= q33
+# asm 1: xor <q33=int64#9,<q33=int64#9
+# asm 2: xor <q33=%r11,<q33=%r11
+xor %r11,%r11
+
+# qhasm: rax = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>rax=int64#7
+# asm 2: movq 24(<xp=%rsi),>rax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU3
+mulq crypto_sign_ed25519_amd64_64_MU3
+
+# qhasm: q23 = rax
+# asm 1: mov <rax=int64#7,>q23=int64#10
+# asm 2: mov <rax=%rax,>q23=%r12
+mov %rax,%r12
+
+# qhasm: c = rdx
+# asm 1: mov <rdx=int64#3,>c=int64#11
+# asm 2: mov <rdx=%rdx,>c=%r13
+mov %rdx,%r13
+
+# qhasm: rax = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>rax=int64#7
+# asm 2: movq 24(<xp=%rsi),>rax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU4
+mulq crypto_sign_ed25519_amd64_64_MU4
+
+# qhasm: q24 = rax
+# asm 1: mov <rax=int64#7,>q24=int64#12
+# asm 2: mov <rax=%rax,>q24=%r14
+mov %rax,%r14
+
+# qhasm: carry? q24 += c
+# asm 1: add <c=int64#11,<q24=int64#12
+# asm 2: add <c=%r13,<q24=%r14
+add %r13,%r14
+
+# qhasm: q30 += rdx + carry
+# asm 1: adc <rdx=int64#3,<q30=int64#5
+# asm 2: adc <rdx=%rdx,<q30=%r8
+adc %rdx,%r8
+
+# qhasm: rax = *(uint64 *)(xp + 32)
+# asm 1: movq 32(<xp=int64#2),>rax=int64#7
+# asm 2: movq 32(<xp=%rsi),>rax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU2
+mulq crypto_sign_ed25519_amd64_64_MU2
+
+# qhasm: carry? q23 += rax
+# asm 1: add <rax=int64#7,<q23=int64#10
+# asm 2: add <rax=%rax,<q23=%r12
+add %rax,%r12
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#11
+# asm 2: mov $0,>c=%r13
+mov $0,%r13
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#11
+# asm 2: adc <rdx=%rdx,<c=%r13
+adc %rdx,%r13
+
+# qhasm: rax = *(uint64 *)(xp + 32)
+# asm 1: movq 32(<xp=int64#2),>rax=int64#7
+# asm 2: movq 32(<xp=%rsi),>rax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU3
+mulq crypto_sign_ed25519_amd64_64_MU3
+
+# qhasm: carry? q24 += rax
+# asm 1: add <rax=int64#7,<q24=int64#12
+# asm 2: add <rax=%rax,<q24=%r14
+add %rax,%r14
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q24 += c
+# asm 1: add <c=int64#11,<q24=int64#12
+# asm 2: add <c=%r13,<q24=%r14
+add %r13,%r14
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#11
+# asm 2: mov $0,>c=%r13
+mov $0,%r13
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#11
+# asm 2: adc <rdx=%rdx,<c=%r13
+adc %rdx,%r13
+
+# qhasm: rax = *(uint64 *)(xp + 32)
+# asm 1: movq 32(<xp=int64#2),>rax=int64#7
+# asm 2: movq 32(<xp=%rsi),>rax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU4
+mulq crypto_sign_ed25519_amd64_64_MU4
+
+# qhasm: carry? q30 += rax
+# asm 1: add <rax=int64#7,<q30=int64#5
+# asm 2: add <rax=%rax,<q30=%r8
+add %rax,%r8
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q30 += c
+# asm 1: add <c=int64#11,<q30=int64#5
+# asm 2: add <c=%r13,<q30=%r8
+add %r13,%r8
+
+# qhasm: q31 += rdx + carry
+# asm 1: adc <rdx=int64#3,<q31=int64#6
+# asm 2: adc <rdx=%rdx,<q31=%r9
+adc %rdx,%r9
+
+# qhasm: rax = *(uint64 *)(xp + 40)
+# asm 1: movq 40(<xp=int64#2),>rax=int64#7
+# asm 2: movq 40(<xp=%rsi),>rax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU1
+mulq crypto_sign_ed25519_amd64_64_MU1
+
+# qhasm: carry? q23 += rax
+# asm 1: add <rax=int64#7,<q23=int64#10
+# asm 2: add <rax=%rax,<q23=%r12
+add %rax,%r12
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#11
+# asm 2: mov $0,>c=%r13
+mov $0,%r13
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#11
+# asm 2: adc <rdx=%rdx,<c=%r13
+adc %rdx,%r13
+
+# qhasm: rax = *(uint64 *)(xp + 40)
+# asm 1: movq 40(<xp=int64#2),>rax=int64#7
+# asm 2: movq 40(<xp=%rsi),>rax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU2
+mulq crypto_sign_ed25519_amd64_64_MU2
+
+# qhasm: carry? q24 += rax
+# asm 1: add <rax=int64#7,<q24=int64#12
+# asm 2: add <rax=%rax,<q24=%r14
+add %rax,%r14
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q24 += c
+# asm 1: add <c=int64#11,<q24=int64#12
+# asm 2: add <c=%r13,<q24=%r14
+add %r13,%r14
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#11
+# asm 2: mov $0,>c=%r13
+mov $0,%r13
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#11
+# asm 2: adc <rdx=%rdx,<c=%r13
+adc %rdx,%r13
+
+# qhasm: rax = *(uint64 *)(xp + 40)
+# asm 1: movq 40(<xp=int64#2),>rax=int64#7
+# asm 2: movq 40(<xp=%rsi),>rax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU3
+mulq crypto_sign_ed25519_amd64_64_MU3
+
+# qhasm: carry? q30 += rax
+# asm 1: add <rax=int64#7,<q30=int64#5
+# asm 2: add <rax=%rax,<q30=%r8
+add %rax,%r8
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q30 += c
+# asm 1: add <c=int64#11,<q30=int64#5
+# asm 2: add <c=%r13,<q30=%r8
+add %r13,%r8
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#11
+# asm 2: mov $0,>c=%r13
+mov $0,%r13
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#11
+# asm 2: adc <rdx=%rdx,<c=%r13
+adc %rdx,%r13
+
+# qhasm: rax = *(uint64 *)(xp + 40)
+# asm 1: movq 40(<xp=int64#2),>rax=int64#7
+# asm 2: movq 40(<xp=%rsi),>rax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU4
+mulq crypto_sign_ed25519_amd64_64_MU4
+
+# qhasm: carry? q31 += rax
+# asm 1: add <rax=int64#7,<q31=int64#6
+# asm 2: add <rax=%rax,<q31=%r9
+add %rax,%r9
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q31 += c
+# asm 1: add <c=int64#11,<q31=int64#6
+# asm 2: add <c=%r13,<q31=%r9
+add %r13,%r9
+
+# qhasm: q32 += rdx + carry
+# asm 1: adc <rdx=int64#3,<q32=int64#8
+# asm 2: adc <rdx=%rdx,<q32=%r10
+adc %rdx,%r10
+
+# qhasm: rax = *(uint64 *)(xp + 48)
+# asm 1: movq 48(<xp=int64#2),>rax=int64#7
+# asm 2: movq 48(<xp=%rsi),>rax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU0
+mulq crypto_sign_ed25519_amd64_64_MU0
+
+# qhasm: carry? q23 += rax
+# asm 1: add <rax=int64#7,<q23=int64#10
+# asm 2: add <rax=%rax,<q23=%r12
+add %rax,%r12
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#10
+# asm 2: mov $0,>c=%r12
+mov $0,%r12
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#10
+# asm 2: adc <rdx=%rdx,<c=%r12
+adc %rdx,%r12
+
+# qhasm: rax = *(uint64 *)(xp + 48)
+# asm 1: movq 48(<xp=int64#2),>rax=int64#7
+# asm 2: movq 48(<xp=%rsi),>rax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU1
+mulq crypto_sign_ed25519_amd64_64_MU1
+
+# qhasm: carry? q24 += rax
+# asm 1: add <rax=int64#7,<q24=int64#12
+# asm 2: add <rax=%rax,<q24=%r14
+add %rax,%r14
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q24 += c
+# asm 1: add <c=int64#10,<q24=int64#12
+# asm 2: add <c=%r12,<q24=%r14
+add %r12,%r14
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#10
+# asm 2: mov $0,>c=%r12
+mov $0,%r12
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#10
+# asm 2: adc <rdx=%rdx,<c=%r12
+adc %rdx,%r12
+
+# qhasm: rax = *(uint64 *)(xp + 48)
+# asm 1: movq 48(<xp=int64#2),>rax=int64#7
+# asm 2: movq 48(<xp=%rsi),>rax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU2
+mulq crypto_sign_ed25519_amd64_64_MU2
+
+# qhasm: carry? q30 += rax
+# asm 1: add <rax=int64#7,<q30=int64#5
+# asm 2: add <rax=%rax,<q30=%r8
+add %rax,%r8
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q30 += c
+# asm 1: add <c=int64#10,<q30=int64#5
+# asm 2: add <c=%r12,<q30=%r8
+add %r12,%r8
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#10
+# asm 2: mov $0,>c=%r12
+mov $0,%r12
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#10
+# asm 2: adc <rdx=%rdx,<c=%r12
+adc %rdx,%r12
+
+# qhasm: rax = *(uint64 *)(xp + 48)
+# asm 1: movq 48(<xp=int64#2),>rax=int64#7
+# asm 2: movq 48(<xp=%rsi),>rax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU3
+mulq crypto_sign_ed25519_amd64_64_MU3
+
+# qhasm: carry? q31 += rax
+# asm 1: add <rax=int64#7,<q31=int64#6
+# asm 2: add <rax=%rax,<q31=%r9
+add %rax,%r9
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q31 += c
+# asm 1: add <c=int64#10,<q31=int64#6
+# asm 2: add <c=%r12,<q31=%r9
+add %r12,%r9
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#10
+# asm 2: mov $0,>c=%r12
+mov $0,%r12
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#10
+# asm 2: adc <rdx=%rdx,<c=%r12
+adc %rdx,%r12
+
+# qhasm: rax = *(uint64 *)(xp + 48)
+# asm 1: movq 48(<xp=int64#2),>rax=int64#7
+# asm 2: movq 48(<xp=%rsi),>rax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU4
+mulq crypto_sign_ed25519_amd64_64_MU4
+
+# qhasm: carry? q32 += rax
+# asm 1: add <rax=int64#7,<q32=int64#8
+# asm 2: add <rax=%rax,<q32=%r10
+add %rax,%r10
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q32 += c
+# asm 1: add <c=int64#10,<q32=int64#8
+# asm 2: add <c=%r12,<q32=%r10
+add %r12,%r10
+
+# qhasm: q33 += rdx + carry
+# asm 1: adc <rdx=int64#3,<q33=int64#9
+# asm 2: adc <rdx=%rdx,<q33=%r11
+adc %rdx,%r11
+
+# qhasm: rax = *(uint64 *)(xp + 56)
+# asm 1: movq 56(<xp=int64#2),>rax=int64#7
+# asm 2: movq 56(<xp=%rsi),>rax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU0
+mulq crypto_sign_ed25519_amd64_64_MU0
+
+# qhasm: carry? q24 += rax
+# asm 1: add <rax=int64#7,<q24=int64#12
+# asm 2: add <rax=%rax,<q24=%r14
+add %rax,%r14
+
+# qhasm: free q24
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#10
+# asm 2: mov $0,>c=%r12
+mov $0,%r12
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#10
+# asm 2: adc <rdx=%rdx,<c=%r12
+adc %rdx,%r12
+
+# qhasm: rax = *(uint64 *)(xp + 56)
+# asm 1: movq 56(<xp=int64#2),>rax=int64#7
+# asm 2: movq 56(<xp=%rsi),>rax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU1
+mulq crypto_sign_ed25519_amd64_64_MU1
+
+# qhasm: carry? q30 += rax
+# asm 1: add <rax=int64#7,<q30=int64#5
+# asm 2: add <rax=%rax,<q30=%r8
+add %rax,%r8
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q30 += c
+# asm 1: add <c=int64#10,<q30=int64#5
+# asm 2: add <c=%r12,<q30=%r8
+add %r12,%r8
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#10
+# asm 2: mov $0,>c=%r12
+mov $0,%r12
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#10
+# asm 2: adc <rdx=%rdx,<c=%r12
+adc %rdx,%r12
+
+# qhasm: q30_stack = q30
+# asm 1: movq <q30=int64#5,>q30_stack=stack64#8
+# asm 2: movq <q30=%r8,>q30_stack=56(%rsp)
+movq %r8,56(%rsp)
+
+# qhasm: rax = *(uint64 *)(xp + 56)
+# asm 1: movq 56(<xp=int64#2),>rax=int64#7
+# asm 2: movq 56(<xp=%rsi),>rax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU2
+mulq crypto_sign_ed25519_amd64_64_MU2
+
+# qhasm: carry? q31 += rax
+# asm 1: add <rax=int64#7,<q31=int64#6
+# asm 2: add <rax=%rax,<q31=%r9
+add %rax,%r9
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q31 += c
+# asm 1: add <c=int64#10,<q31=int64#6
+# asm 2: add <c=%r12,<q31=%r9
+add %r12,%r9
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#5
+# asm 2: mov $0,>c=%r8
+mov $0,%r8
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#5
+# asm 2: adc <rdx=%rdx,<c=%r8
+adc %rdx,%r8
+
+# qhasm: q31_stack = q31
+# asm 1: movq <q31=int64#6,>q31_stack=stack64#9
+# asm 2: movq <q31=%r9,>q31_stack=64(%rsp)
+movq %r9,64(%rsp)
+
+# qhasm: rax = *(uint64 *)(xp + 56)
+# asm 1: movq 56(<xp=int64#2),>rax=int64#7
+# asm 2: movq 56(<xp=%rsi),>rax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU3
+mulq crypto_sign_ed25519_amd64_64_MU3
+
+# qhasm: carry? q32 += rax
+# asm 1: add <rax=int64#7,<q32=int64#8
+# asm 2: add <rax=%rax,<q32=%r10
+add %rax,%r10
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q32 += c
+# asm 1: add <c=int64#5,<q32=int64#8
+# asm 2: add <c=%r8,<q32=%r10
+add %r8,%r10
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#5
+# asm 2: mov $0,>c=%r8
+mov $0,%r8
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#5
+# asm 2: adc <rdx=%rdx,<c=%r8
+adc %rdx,%r8
+
+# qhasm: q32_stack = q32
+# asm 1: movq <q32=int64#8,>q32_stack=stack64#10
+# asm 2: movq <q32=%r10,>q32_stack=72(%rsp)
+movq %r10,72(%rsp)
+
+# qhasm: rax = *(uint64 *)(xp + 56)
+# asm 1: movq 56(<xp=int64#2),>rax=int64#7
+# asm 2: movq 56(<xp=%rsi),>rax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU4
+mulq crypto_sign_ed25519_amd64_64_MU4
+
+# qhasm: carry? q33 += rax
+# asm 1: add <rax=int64#7,<q33=int64#9
+# asm 2: add <rax=%rax,<q33=%r11
+add %rax,%r11
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: q33 += c
+# asm 1: add <c=int64#5,<q33=int64#9
+# asm 2: add <c=%r8,<q33=%r11
+add %r8,%r11
+
+# qhasm: q33_stack = q33
+# asm 1: movq <q33=int64#9,>q33_stack=stack64#11
+# asm 2: movq <q33=%r11,>q33_stack=80(%rsp)
+movq %r11,80(%rsp)
+
+# qhasm: rax = q30_stack
+# asm 1: movq <q30_stack=stack64#8,>rax=int64#7
+# asm 2: movq <q30_stack=56(%rsp),>rax=%rax
+movq 56(%rsp),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER0
+mulq crypto_sign_ed25519_amd64_64_ORDER0
+
+# qhasm: r20 = rax
+# asm 1: mov <rax=int64#7,>r20=int64#5
+# asm 2: mov <rax=%rax,>r20=%r8
+mov %rax,%r8
+
+# qhasm: c = rdx
+# asm 1: mov <rdx=int64#3,>c=int64#6
+# asm 2: mov <rdx=%rdx,>c=%r9
+mov %rdx,%r9
+
+# qhasm: rax = q30_stack
+# asm 1: movq <q30_stack=stack64#8,>rax=int64#7
+# asm 2: movq <q30_stack=56(%rsp),>rax=%rax
+movq 56(%rsp),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER1
+mulq crypto_sign_ed25519_amd64_64_ORDER1
+
+# qhasm: r21 = rax
+# asm 1: mov <rax=int64#7,>r21=int64#8
+# asm 2: mov <rax=%rax,>r21=%r10
+mov %rax,%r10
+
+# qhasm: carry? r21 += c
+# asm 1: add <c=int64#6,<r21=int64#8
+# asm 2: add <c=%r9,<r21=%r10
+add %r9,%r10
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#6
+# asm 2: mov $0,>c=%r9
+mov $0,%r9
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#6
+# asm 2: adc <rdx=%rdx,<c=%r9
+adc %rdx,%r9
+
+# qhasm: rax = q30_stack
+# asm 1: movq <q30_stack=stack64#8,>rax=int64#7
+# asm 2: movq <q30_stack=56(%rsp),>rax=%rax
+movq 56(%rsp),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER2
+mulq crypto_sign_ed25519_amd64_64_ORDER2
+
+# qhasm: r22 = rax
+# asm 1: mov <rax=int64#7,>r22=int64#9
+# asm 2: mov <rax=%rax,>r22=%r11
+mov %rax,%r11
+
+# qhasm: carry? r22 += c
+# asm 1: add <c=int64#6,<r22=int64#9
+# asm 2: add <c=%r9,<r22=%r11
+add %r9,%r11
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#6
+# asm 2: mov $0,>c=%r9
+mov $0,%r9
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#6
+# asm 2: adc <rdx=%rdx,<c=%r9
+adc %rdx,%r9
+
+# qhasm: rax = q30_stack
+# asm 1: movq <q30_stack=stack64#8,>rax=int64#7
+# asm 2: movq <q30_stack=56(%rsp),>rax=%rax
+movq 56(%rsp),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER3
+mulq crypto_sign_ed25519_amd64_64_ORDER3
+
+# qhasm: free rdx
+
+# qhasm: r23 = rax
+# asm 1: mov <rax=int64#7,>r23=int64#10
+# asm 2: mov <rax=%rax,>r23=%r12
+mov %rax,%r12
+
+# qhasm: r23 += c
+# asm 1: add <c=int64#6,<r23=int64#10
+# asm 2: add <c=%r9,<r23=%r12
+add %r9,%r12
+
+# qhasm: rax = q31_stack
+# asm 1: movq <q31_stack=stack64#9,>rax=int64#7
+# asm 2: movq <q31_stack=64(%rsp),>rax=%rax
+movq 64(%rsp),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER0
+mulq crypto_sign_ed25519_amd64_64_ORDER0
+
+# qhasm: carry? r21 += rax
+# asm 1: add <rax=int64#7,<r21=int64#8
+# asm 2: add <rax=%rax,<r21=%r10
+add %rax,%r10
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#6
+# asm 2: mov $0,>c=%r9
+mov $0,%r9
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#6
+# asm 2: adc <rdx=%rdx,<c=%r9
+adc %rdx,%r9
+
+# qhasm: rax = q31_stack
+# asm 1: movq <q31_stack=stack64#9,>rax=int64#7
+# asm 2: movq <q31_stack=64(%rsp),>rax=%rax
+movq 64(%rsp),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER1
+mulq crypto_sign_ed25519_amd64_64_ORDER1
+
+# qhasm: carry? r22 += rax
+# asm 1: add <rax=int64#7,<r22=int64#9
+# asm 2: add <rax=%rax,<r22=%r11
+add %rax,%r11
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? r22 += c
+# asm 1: add <c=int64#6,<r22=int64#9
+# asm 2: add <c=%r9,<r22=%r11
+add %r9,%r11
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#4
+# asm 2: mov $0,>c=%rcx
+mov $0,%rcx
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#4
+# asm 2: adc <rdx=%rdx,<c=%rcx
+adc %rdx,%rcx
+
+# qhasm: rax = q31_stack
+# asm 1: movq <q31_stack=stack64#9,>rax=int64#7
+# asm 2: movq <q31_stack=64(%rsp),>rax=%rax
+movq 64(%rsp),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER2
+mulq crypto_sign_ed25519_amd64_64_ORDER2
+
+# qhasm: free rdx
+
+# qhasm: r23 += rax
+# asm 1: add <rax=int64#7,<r23=int64#10
+# asm 2: add <rax=%rax,<r23=%r12
+add %rax,%r12
+
+# qhasm: r23 += c
+# asm 1: add <c=int64#4,<r23=int64#10
+# asm 2: add <c=%rcx,<r23=%r12
+add %rcx,%r12
+
+# qhasm: rax = q32_stack
+# asm 1: movq <q32_stack=stack64#10,>rax=int64#7
+# asm 2: movq <q32_stack=72(%rsp),>rax=%rax
+movq 72(%rsp),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER0
+mulq crypto_sign_ed25519_amd64_64_ORDER0
+
+# qhasm: carry? r22 += rax
+# asm 1: add <rax=int64#7,<r22=int64#9
+# asm 2: add <rax=%rax,<r22=%r11
+add %rax,%r11
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#4
+# asm 2: mov $0,>c=%rcx
+mov $0,%rcx
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#4
+# asm 2: adc <rdx=%rdx,<c=%rcx
+adc %rdx,%rcx
+
+# qhasm: rax = q32_stack
+# asm 1: movq <q32_stack=stack64#10,>rax=int64#7
+# asm 2: movq <q32_stack=72(%rsp),>rax=%rax
+movq 72(%rsp),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER1
+mulq crypto_sign_ed25519_amd64_64_ORDER1
+
+# qhasm: free rdx
+
+# qhasm: r23 += rax
+# asm 1: add <rax=int64#7,<r23=int64#10
+# asm 2: add <rax=%rax,<r23=%r12
+add %rax,%r12
+
+# qhasm: r23 += c
+# asm 1: add <c=int64#4,<r23=int64#10
+# asm 2: add <c=%rcx,<r23=%r12
+add %rcx,%r12
+
+# qhasm: rax = q33_stack
+# asm 1: movq <q33_stack=stack64#11,>rax=int64#7
+# asm 2: movq <q33_stack=80(%rsp),>rax=%rax
+movq 80(%rsp),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER0
+mulq crypto_sign_ed25519_amd64_64_ORDER0
+
+# qhasm: free rdx
+
+# qhasm: r23 += rax
+# asm 1: add <rax=int64#7,<r23=int64#10
+# asm 2: add <rax=%rax,<r23=%r12
+add %rax,%r12
+
+# qhasm: r0 = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#2),>r0=int64#3
+# asm 2: movq 0(<xp=%rsi),>r0=%rdx
+movq 0(%rsi),%rdx
+
+# qhasm: carry? r0 -= r20
+# asm 1: sub <r20=int64#5,<r0=int64#3
+# asm 2: sub <r20=%r8,<r0=%rdx
+sub %r8,%rdx
+
+# qhasm: t0 = r0
+# asm 1: mov <r0=int64#3,>t0=int64#4
+# asm 2: mov <r0=%rdx,>t0=%rcx
+mov %rdx,%rcx
+
+# qhasm: r1 = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>r1=int64#5
+# asm 2: movq 8(<xp=%rsi),>r1=%r8
+movq 8(%rsi),%r8
+
+# qhasm: carry? r1 -= r21 - carry
+# asm 1: sbb <r21=int64#8,<r1=int64#5
+# asm 2: sbb <r21=%r10,<r1=%r8
+sbb %r10,%r8
+
+# qhasm: t1 = r1
+# asm 1: mov <r1=int64#5,>t1=int64#6
+# asm 2: mov <r1=%r8,>t1=%r9
+mov %r8,%r9
+
+# qhasm: r2 = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>r2=int64#7
+# asm 2: movq 16(<xp=%rsi),>r2=%rax
+movq 16(%rsi),%rax
+
+# qhasm: carry? r2 -= r22 - carry
+# asm 1: sbb <r22=int64#9,<r2=int64#7
+# asm 2: sbb <r22=%r11,<r2=%rax
+sbb %r11,%rax
+
+# qhasm: t2 = r2
+# asm 1: mov <r2=int64#7,>t2=int64#8
+# asm 2: mov <r2=%rax,>t2=%r10
+mov %rax,%r10
+
+# qhasm: r3 = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>r3=int64#2
+# asm 2: movq 24(<xp=%rsi),>r3=%rsi
+movq 24(%rsi),%rsi
+
+# qhasm: r3 -= r23 - carry
+# asm 1: sbb <r23=int64#10,<r3=int64#2
+# asm 2: sbb <r23=%r12,<r3=%rsi
+sbb %r12,%rsi
+
+# qhasm: t3 = r3
+# asm 1: mov <r3=int64#2,>t3=int64#9
+# asm 2: mov <r3=%rsi,>t3=%r11
+mov %rsi,%r11
+
+# qhasm: carry? t0 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER0
+# asm 1: sub crypto_sign_ed25519_amd64_64_ORDER0,<t0=int64#4
+# asm 2: sub crypto_sign_ed25519_amd64_64_ORDER0,<t0=%rcx
+sub crypto_sign_ed25519_amd64_64_ORDER0,%rcx
+
+# qhasm: carry? t1 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER1 - carry
+# asm 1: sbb crypto_sign_ed25519_amd64_64_ORDER1,<t1=int64#6
+# asm 2: sbb crypto_sign_ed25519_amd64_64_ORDER1,<t1=%r9
+sbb crypto_sign_ed25519_amd64_64_ORDER1,%r9
+
+# qhasm: carry? t2 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER2 - carry
+# asm 1: sbb crypto_sign_ed25519_amd64_64_ORDER2,<t2=int64#8
+# asm 2: sbb crypto_sign_ed25519_amd64_64_ORDER2,<t2=%r10
+sbb crypto_sign_ed25519_amd64_64_ORDER2,%r10
+
+# qhasm: unsigned<? t3 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER3 - carry
+# asm 1: sbb crypto_sign_ed25519_amd64_64_ORDER3,<t3=int64#9
+# asm 2: sbb crypto_sign_ed25519_amd64_64_ORDER3,<t3=%r11
+sbb crypto_sign_ed25519_amd64_64_ORDER3,%r11
+
+# qhasm: r0 = t0 if !unsigned<
+# asm 1: cmovae <t0=int64#4,<r0=int64#3
+# asm 2: cmovae <t0=%rcx,<r0=%rdx
+cmovae %rcx,%rdx
+
+# qhasm: t0 = r0
+# asm 1: mov <r0=int64#3,>t0=int64#4
+# asm 2: mov <r0=%rdx,>t0=%rcx
+mov %rdx,%rcx
+
+# qhasm: r1 = t1 if !unsigned<
+# asm 1: cmovae <t1=int64#6,<r1=int64#5
+# asm 2: cmovae <t1=%r9,<r1=%r8
+cmovae %r9,%r8
+
+# qhasm: t1 = r1
+# asm 1: mov <r1=int64#5,>t1=int64#6
+# asm 2: mov <r1=%r8,>t1=%r9
+mov %r8,%r9
+
+# qhasm: r2 = t2 if !unsigned<
+# asm 1: cmovae <t2=int64#8,<r2=int64#7
+# asm 2: cmovae <t2=%r10,<r2=%rax
+cmovae %r10,%rax
+
+# qhasm: t2 = r2
+# asm 1: mov <r2=int64#7,>t2=int64#8
+# asm 2: mov <r2=%rax,>t2=%r10
+mov %rax,%r10
+
+# qhasm: r3 = t3 if !unsigned<
+# asm 1: cmovae <t3=int64#9,<r3=int64#2
+# asm 2: cmovae <t3=%r11,<r3=%rsi
+cmovae %r11,%rsi
+
+# qhasm: t3 = r3
+# asm 1: mov <r3=int64#2,>t3=int64#9
+# asm 2: mov <r3=%rsi,>t3=%r11
+mov %rsi,%r11
+
+# qhasm: carry? t0 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER0
+# asm 1: sub crypto_sign_ed25519_amd64_64_ORDER0,<t0=int64#4
+# asm 2: sub crypto_sign_ed25519_amd64_64_ORDER0,<t0=%rcx
+sub crypto_sign_ed25519_amd64_64_ORDER0,%rcx
+
+# qhasm: carry? t1 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER1 - carry
+# asm 1: sbb crypto_sign_ed25519_amd64_64_ORDER1,<t1=int64#6
+# asm 2: sbb crypto_sign_ed25519_amd64_64_ORDER1,<t1=%r9
+sbb crypto_sign_ed25519_amd64_64_ORDER1,%r9
+
+# qhasm: carry? t2 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER2 - carry
+# asm 1: sbb crypto_sign_ed25519_amd64_64_ORDER2,<t2=int64#8
+# asm 2: sbb crypto_sign_ed25519_amd64_64_ORDER2,<t2=%r10
+sbb crypto_sign_ed25519_amd64_64_ORDER2,%r10
+
+# qhasm: unsigned<? t3 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER3 - carry
+# asm 1: sbb crypto_sign_ed25519_amd64_64_ORDER3,<t3=int64#9
+# asm 2: sbb crypto_sign_ed25519_amd64_64_ORDER3,<t3=%r11
+sbb crypto_sign_ed25519_amd64_64_ORDER3,%r11
+
+# qhasm: r0 = t0 if !unsigned<
+# asm 1: cmovae <t0=int64#4,<r0=int64#3
+# asm 2: cmovae <t0=%rcx,<r0=%rdx
+cmovae %rcx,%rdx
+
+# qhasm: r1 = t1 if !unsigned<
+# asm 1: cmovae <t1=int64#6,<r1=int64#5
+# asm 2: cmovae <t1=%r9,<r1=%r8
+cmovae %r9,%r8
+
+# qhasm: r2 = t2 if !unsigned<
+# asm 1: cmovae <t2=int64#8,<r2=int64#7
+# asm 2: cmovae <t2=%r10,<r2=%rax
+cmovae %r10,%rax
+
+# qhasm: r3 = t3 if !unsigned<
+# asm 1: cmovae <t3=int64#9,<r3=int64#2
+# asm 2: cmovae <t3=%r11,<r3=%rsi
+cmovae %r11,%rsi
+
+# qhasm: *(uint64 *)(rp + 0) = r0
+# asm 1: movq <r0=int64#3,0(<rp=int64#1)
+# asm 2: movq <r0=%rdx,0(<rp=%rdi)
+movq %rdx,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = r1
+# asm 1: movq <r1=int64#5,8(<rp=int64#1)
+# asm 2: movq <r1=%r8,8(<rp=%rdi)
+movq %r8,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = r2
+# asm 1: movq <r2=int64#7,16(<rp=int64#1)
+# asm 2: movq <r2=%rax,16(<rp=%rdi)
+movq %rax,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = r3
+# asm 1: movq <r3=int64#2,24(<rp=int64#1)
+# asm 2: movq <r3=%rsi,24(<rp=%rdi)
+movq %rsi,24(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/sc25519_from32bytes.c b/ext/ed25519-amd64-asm/sc25519_from32bytes.c
new file mode 100644
index 00000000..7f21e686
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_from32bytes.c
@@ -0,0 +1,55 @@
+#include "sc25519.h"
+
+/*Arithmetic modulo the group order n = 2^252 + 27742317777372353535851937790883648493
+ * = 7237005577332262213973186563042994240857116359379907606001950938285454250989
+ */
+
+/* Contains order, 2*order, 4*order, 8*order, each represented in 4 consecutive unsigned long long */
+static const unsigned long long order[16] = {0x5812631A5CF5D3EDULL, 0x14DEF9DEA2F79CD6ULL,
+ 0x0000000000000000ULL, 0x1000000000000000ULL,
+ 0xB024C634B9EBA7DAULL, 0x29BDF3BD45EF39ACULL,
+ 0x0000000000000000ULL, 0x2000000000000000ULL,
+ 0x60498C6973D74FB4ULL, 0x537BE77A8BDE7359ULL,
+ 0x0000000000000000ULL, 0x4000000000000000ULL,
+ 0xC09318D2E7AE9F68ULL, 0xA6F7CEF517BCE6B2ULL,
+ 0x0000000000000000ULL, 0x8000000000000000ULL};
+
+static unsigned long long smaller(unsigned long long a,unsigned long long b)
+{
+ unsigned long long atop = a >> 32;
+ unsigned long long abot = a & 4294967295;
+ unsigned long long btop = b >> 32;
+ unsigned long long bbot = b & 4294967295;
+ unsigned long long atopbelowbtop = (atop - btop) >> 63;
+ unsigned long long atopeqbtop = ((atop ^ btop) - 1) >> 63;
+ unsigned long long abotbelowbbot = (abot - bbot) >> 63;
+ return atopbelowbtop | (atopeqbtop & abotbelowbbot);
+}
+
+void sc25519_from32bytes(sc25519 *r, const unsigned char x[32])
+{
+ unsigned long long t[4];
+ unsigned long long b;
+ unsigned long long mask;
+ int i, j;
+
+ /* assuming little-endian */
+ r->v[0] = *(unsigned long long *)x;
+ r->v[1] = *(((unsigned long long *)x)+1);
+ r->v[2] = *(((unsigned long long *)x)+2);
+ r->v[3] = *(((unsigned long long *)x)+3);
+
+ for(j=3;j>=0;j--)
+ {
+ b=0;
+ for(i=0;i<4;i++)
+ {
+ b += order[4*j+i]; /* no overflow for this particular order */
+ t[i] = r->v[i] - b;
+ b = smaller(r->v[i],b);
+ }
+ mask = b - 1;
+ for(i=0;i<4;i++)
+ r->v[i] ^= mask & (r->v[i] ^ t[i]);
+ }
+}
diff --git a/ext/ed25519-amd64-asm/sc25519_from64bytes.c b/ext/ed25519-amd64-asm/sc25519_from64bytes.c
new file mode 100644
index 00000000..8e76a1b3
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_from64bytes.c
@@ -0,0 +1,7 @@
+#include "sc25519.h"
+
+void sc25519_from64bytes(sc25519 *r, const unsigned char x[64])
+{
+ /* assuming little-endian representation of unsigned long long */
+ sc25519_barrett(r, (unsigned long long *)x);
+}
diff --git a/ext/ed25519-amd64-asm/sc25519_from_shortsc.c b/ext/ed25519-amd64-asm/sc25519_from_shortsc.c
new file mode 100644
index 00000000..3b8ff2fb
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_from_shortsc.c
@@ -0,0 +1,9 @@
+#include "sc25519.h"
+
+void sc25519_from_shortsc(sc25519 *r, const shortsc25519 *x)
+{
+ r->v[0] = x->v[0];
+ r->v[1] = x->v[1];
+ r->v[2] = 0;
+ r->v[3] = 0;
+}
diff --git a/ext/ed25519-amd64-asm/sc25519_iszero.c b/ext/ed25519-amd64-asm/sc25519_iszero.c
new file mode 100644
index 00000000..21f593d7
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_iszero.c
@@ -0,0 +1,10 @@
+#include "sc25519.h"
+
+int sc25519_iszero_vartime(const sc25519 *x)
+{
+ if(x->v[0] != 0) return 0;
+ if(x->v[1] != 0) return 0;
+ if(x->v[2] != 0) return 0;
+ if(x->v[3] != 0) return 0;
+ return 1;
+}
diff --git a/ext/ed25519-amd64-asm/sc25519_lt.s b/ext/ed25519-amd64-asm/sc25519_lt.s
new file mode 100644
index 00000000..3ba43178
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_lt.s
@@ -0,0 +1,131 @@
+
+# qhasm: int64 xp
+
+# qhasm: int64 yp
+
+# qhasm: int64 ret
+
+# qhasm: input xp
+
+# qhasm: input yp
+
+# qhasm: output ret
+
+# qhasm: int64 t0
+
+# qhasm: int64 t1
+
+# qhasm: int64 t2
+
+# qhasm: int64 t3
+
+# qhasm: int64 doof
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_sc25519_lt
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_sc25519_lt
+.globl crypto_sign_ed25519_amd64_64_sc25519_lt
+_crypto_sign_ed25519_amd64_64_sc25519_lt:
+crypto_sign_ed25519_amd64_64_sc25519_lt:
+mov %rsp,%r11
+and $31,%r11
+add $0,%r11
+sub %r11,%rsp
+
+# qhasm: t0 = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#1),>t0=int64#3
+# asm 2: movq 0(<xp=%rdi),>t0=%rdx
+movq 0(%rdi),%rdx
+
+# qhasm: t1 = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#1),>t1=int64#4
+# asm 2: movq 8(<xp=%rdi),>t1=%rcx
+movq 8(%rdi),%rcx
+
+# qhasm: t2 = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#1),>t2=int64#5
+# asm 2: movq 16(<xp=%rdi),>t2=%r8
+movq 16(%rdi),%r8
+
+# qhasm: t3 = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#1),>t3=int64#1
+# asm 2: movq 24(<xp=%rdi),>t3=%rdi
+movq 24(%rdi),%rdi
+
+# qhasm: carry? t0 -= *(uint64 *)(yp + 0)
+# asm 1: subq 0(<yp=int64#2),<t0=int64#3
+# asm 2: subq 0(<yp=%rsi),<t0=%rdx
+subq 0(%rsi),%rdx
+
+# qhasm: carry? t1 -= *(uint64 *)(yp + 8) - carry
+# asm 1: sbbq 8(<yp=int64#2),<t1=int64#4
+# asm 2: sbbq 8(<yp=%rsi),<t1=%rcx
+sbbq 8(%rsi),%rcx
+
+# qhasm: carry? t2 -= *(uint64 *)(yp + 16) - carry
+# asm 1: sbbq 16(<yp=int64#2),<t2=int64#5
+# asm 2: sbbq 16(<yp=%rsi),<t2=%r8
+sbbq 16(%rsi),%r8
+
+# qhasm: carry? t3 -= *(uint64 *)(yp + 24) - carry
+# asm 1: sbbq 24(<yp=int64#2),<t3=int64#1
+# asm 2: sbbq 24(<yp=%rsi),<t3=%rdi
+sbbq 24(%rsi),%rdi
+
+# qhasm: ret = 0
+# asm 1: mov $0,>ret=int64#1
+# asm 2: mov $0,>ret=%rdi
+mov $0,%rdi
+
+# qhasm: doof = 1
+# asm 1: mov $1,>doof=int64#2
+# asm 2: mov $1,>doof=%rsi
+mov $1,%rsi
+
+# qhasm: ret = doof if carry
+# asm 1: cmovc <doof=int64#2,<ret=int64#1
+# asm 2: cmovc <doof=%rsi,<ret=%rdi
+cmovc %rsi,%rdi
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/sc25519_mul.c b/ext/ed25519-amd64-asm/sc25519_mul.c
new file mode 100644
index 00000000..ca4d5baa
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_mul.c
@@ -0,0 +1,12 @@
+#include "sc25519.h"
+
+#define ull4_mul crypto_sign_ed25519_amd64_64_ull4_mul
+
+extern void ull4_mul(unsigned long long r[8], const unsigned long long x[4], const unsigned long long y[4]);
+
+void sc25519_mul(sc25519 *r, const sc25519 *x, const sc25519 *y)
+{
+ unsigned long long t[8];
+ ull4_mul(t, x->v, y->v);
+ sc25519_barrett(r, t);
+}
diff --git a/ext/ed25519-amd64-asm/sc25519_mul_shortsc.c b/ext/ed25519-amd64-asm/sc25519_mul_shortsc.c
new file mode 100644
index 00000000..0c67250d
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_mul_shortsc.c
@@ -0,0 +1,9 @@
+#include "sc25519.h"
+
+void sc25519_mul_shortsc(sc25519 *r, const sc25519 *x, const shortsc25519 *y)
+{
+ /* XXX: This wants to be faster */
+ sc25519 t;
+ sc25519_from_shortsc(&t, y);
+ sc25519_mul(r, x, &t);
+}
diff --git a/ext/ed25519-amd64-asm/sc25519_slide.c b/ext/ed25519-amd64-asm/sc25519_slide.c
new file mode 100644
index 00000000..4e52010d
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_slide.c
@@ -0,0 +1,49 @@
+#include "sc25519.h"
+
+void sc25519_slide(signed char r[256], const sc25519 *s, int swindowsize)
+{
+ int i,j,k,b,m=(1<<(swindowsize-1))-1, soplen=256;
+ unsigned long long sv0 = s->v[0];
+ unsigned long long sv1 = s->v[1];
+ unsigned long long sv2 = s->v[2];
+ unsigned long long sv3 = s->v[3];
+
+ /* first put the binary expansion into r */
+ for(i=0;i<64;i++) {
+ r[i] = sv0 & 1;
+ r[i+64] = sv1 & 1;
+ r[i+128] = sv2 & 1;
+ r[i+192] = sv3 & 1;
+ sv0 >>= 1;
+ sv1 >>= 1;
+ sv2 >>= 1;
+ sv3 >>= 1;
+ }
+
+ /* Making it sliding window */
+ for (j = 0;j < soplen;++j)
+ {
+ if (r[j]) {
+ for (b = 1;b < soplen - j && b <= 6;++b) {
+ if (r[j] + (r[j + b] << b) <= m)
+ {
+ r[j] += r[j + b] << b; r[j + b] = 0;
+ }
+ else if (r[j] - (r[j + b] << b) >= -m)
+ {
+ r[j] -= r[j + b] << b;
+ for (k = j + b;k < soplen;++k)
+ {
+ if (!r[k]) {
+ r[k] = 1;
+ break;
+ }
+ r[k] = 0;
+ }
+ }
+ else if (r[j + b])
+ break;
+ }
+ }
+ }
+}
diff --git a/ext/ed25519-amd64-asm/sc25519_sub_nored.s b/ext/ed25519-amd64-asm/sc25519_sub_nored.s
new file mode 100644
index 00000000..a347e7d4
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_sub_nored.s
@@ -0,0 +1,142 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 xp
+
+# qhasm: int64 yp
+
+# qhasm: input rp
+
+# qhasm: input xp
+
+# qhasm: input yp
+
+# qhasm: int64 r0
+
+# qhasm: int64 r1
+
+# qhasm: int64 r2
+
+# qhasm: int64 r3
+
+# qhasm: int64 t0
+
+# qhasm: int64 t1
+
+# qhasm: int64 t2
+
+# qhasm: int64 t3
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_sc25519_sub_nored
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_sc25519_sub_nored
+.globl crypto_sign_ed25519_amd64_64_sc25519_sub_nored
+_crypto_sign_ed25519_amd64_64_sc25519_sub_nored:
+crypto_sign_ed25519_amd64_64_sc25519_sub_nored:
+mov %rsp,%r11
+and $31,%r11
+add $0,%r11
+sub %r11,%rsp
+
+# qhasm: r0 = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#2),>r0=int64#4
+# asm 2: movq 0(<xp=%rsi),>r0=%rcx
+movq 0(%rsi),%rcx
+
+# qhasm: r1 = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>r1=int64#5
+# asm 2: movq 8(<xp=%rsi),>r1=%r8
+movq 8(%rsi),%r8
+
+# qhasm: r2 = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>r2=int64#6
+# asm 2: movq 16(<xp=%rsi),>r2=%r9
+movq 16(%rsi),%r9
+
+# qhasm: r3 = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>r3=int64#2
+# asm 2: movq 24(<xp=%rsi),>r3=%rsi
+movq 24(%rsi),%rsi
+
+# qhasm: carry? r0 -= *(uint64 *)(yp + 0)
+# asm 1: subq 0(<yp=int64#3),<r0=int64#4
+# asm 2: subq 0(<yp=%rdx),<r0=%rcx
+subq 0(%rdx),%rcx
+
+# qhasm: carry? r1 -= *(uint64 *)(yp + 8) - carry
+# asm 1: sbbq 8(<yp=int64#3),<r1=int64#5
+# asm 2: sbbq 8(<yp=%rdx),<r1=%r8
+sbbq 8(%rdx),%r8
+
+# qhasm: carry? r2 -= *(uint64 *)(yp + 16) - carry
+# asm 1: sbbq 16(<yp=int64#3),<r2=int64#6
+# asm 2: sbbq 16(<yp=%rdx),<r2=%r9
+sbbq 16(%rdx),%r9
+
+# qhasm: r3 -= *(uint64 *)(yp + 24) - carry
+# asm 1: sbbq 24(<yp=int64#3),<r3=int64#2
+# asm 2: sbbq 24(<yp=%rdx),<r3=%rsi
+sbbq 24(%rdx),%rsi
+
+# qhasm: *(uint64 *)(rp + 0) = r0
+# asm 1: movq <r0=int64#4,0(<rp=int64#1)
+# asm 2: movq <r0=%rcx,0(<rp=%rdi)
+movq %rcx,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = r1
+# asm 1: movq <r1=int64#5,8(<rp=int64#1)
+# asm 2: movq <r1=%r8,8(<rp=%rdi)
+movq %r8,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = r2
+# asm 1: movq <r2=int64#6,16(<rp=int64#1)
+# asm 2: movq <r2=%r9,16(<rp=%rdi)
+movq %r9,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = r3
+# asm 1: movq <r3=int64#2,24(<rp=int64#1)
+# asm 2: movq <r3=%rsi,24(<rp=%rdi)
+movq %rsi,24(%rdi)
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/sc25519_to32bytes.c b/ext/ed25519-amd64-asm/sc25519_to32bytes.c
new file mode 100644
index 00000000..eddb235d
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_to32bytes.c
@@ -0,0 +1,8 @@
+#include "sc25519.h"
+
+void sc25519_to32bytes(unsigned char r[32], const sc25519 *x)
+{
+ /* assuming little-endian */
+ int i;
+ for(i=0;i<32;i++) r[i] = i[(unsigned char *)x->v];
+}
diff --git a/ext/ed25519-amd64-asm/sc25519_window4.c b/ext/ed25519-amd64-asm/sc25519_window4.c
new file mode 100644
index 00000000..683a1d4b
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_window4.c
@@ -0,0 +1,27 @@
+#include "sc25519.h"
+
+void sc25519_window4(signed char r[64], const sc25519 *s)
+{
+ char carry;
+ int i;
+ for(i=0;i<16;i++)
+ r[i] = (s->v[0] >> (4*i)) & 15;
+ for(i=0;i<16;i++)
+ r[i+16] = (s->v[1] >> (4*i)) & 15;
+ for(i=0;i<16;i++)
+ r[i+32] = (s->v[2] >> (4*i)) & 15;
+ for(i=0;i<16;i++)
+ r[i+48] = (s->v[3] >> (4*i)) & 15;
+
+ /* Making it signed */
+ carry = 0;
+ for(i=0;i<63;i++)
+ {
+ r[i] += carry;
+ r[i+1] += r[i] >> 4;
+ r[i] &= 15;
+ carry = r[i] >> 3;
+ r[i] -= carry << 4;
+ }
+ r[63] += carry;
+}
diff --git a/ext/ed25519-amd64-asm/sign.c b/ext/ed25519-amd64-asm/sign.c
new file mode 100644
index 00000000..958e4a14
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sign.c
@@ -0,0 +1,165 @@
+#include <stdlib.h>
+#include <string.h>
+/*#include "crypto_sign.h"
+#include "crypto_hash_sha512.h"*/
+#include "ge25519.h"
+
+/* Original */
+#if 0
+int crypto_sign(
+ unsigned char *sm,unsigned long long *smlen,
+ const unsigned char *m,unsigned long long mlen,
+ const unsigned char *sk
+ )
+{
+ unsigned char pk[32];
+ unsigned char az[64];
+ unsigned char nonce[64];
+ unsigned char hram[64];
+ sc25519 sck, scs, scsk;
+ ge25519 ger;
+
+ memmove(pk,sk + 32,32);
+ /* pk: 32-byte public key A */
+
+ crypto_hash_sha512(az,sk,32);
+ az[0] &= 248;
+ az[31] &= 127;
+ az[31] |= 64;
+ /* az: 32-byte scalar a, 32-byte randomizer z */
+
+ *smlen = mlen + 64;
+ memmove(sm + 64,m,mlen);
+ memmove(sm + 32,az + 32,32);
+ /* sm: 32-byte uninit, 32-byte z, mlen-byte m */
+
+ crypto_hash_sha512(nonce, sm+32, mlen+32);
+ /* nonce: 64-byte H(z,m) */
+
+ sc25519_from64bytes(&sck, nonce);
+ ge25519_scalarmult_base(&ger, &sck);
+ ge25519_pack(sm, &ger);
+ /* sm: 32-byte R, 32-byte z, mlen-byte m */
+
+ memmove(sm + 32,pk,32);
+ /* sm: 32-byte R, 32-byte A, mlen-byte m */
+
+ crypto_hash_sha512(hram,sm,mlen + 64);
+ /* hram: 64-byte H(R,A,m) */
+
+ sc25519_from64bytes(&scs, hram);
+ sc25519_from32bytes(&scsk, az);
+ sc25519_mul(&scs, &scs, &scsk);
+ sc25519_add(&scs, &scs, &sck);
+ /* scs: S = nonce + H(R,A,m)a */
+
+ sc25519_to32bytes(sm + 32,&scs);
+ /* sm: 32-byte R, 32-byte S, mlen-byte m */
+
+ return 0;
+}
+#endif
+
+#if 0
+void C25519::sign(const C25519::Private &myPrivate,const C25519::Public &myPublic,const void *msg,unsigned int len,void *signature)
+{
+ sc25519 sck, scs, scsk;
+ ge25519 ger;
+ unsigned char r[32];
+ unsigned char s[32];
+ unsigned char extsk[64];
+ unsigned char hmg[crypto_hash_sha512_BYTES];
+ unsigned char hram[crypto_hash_sha512_BYTES];
+ unsigned char *sig = (unsigned char *)signature;
+ unsigned char digest[64]; // we sign the first 32 bytes of SHA-512(msg)
+
+ SHA512::hash(digest,msg,len);
+
+ SHA512::hash(extsk,myPrivate.data + 32,32);
+ extsk[0] &= 248;
+ extsk[31] &= 127;
+ extsk[31] |= 64;
+
+ for(unsigned int i=0;i<32;i++)
+ sig[32 + i] = extsk[32 + i];
+ for(unsigned int i=0;i<32;i++)
+ sig[64 + i] = digest[i];
+
+ SHA512::hash(hmg,sig + 32,64);
+
+ /* Computation of R */
+ sc25519_from64bytes(&sck, hmg);
+ ge25519_scalarmult_base(&ger, &sck);
+ ge25519_pack(r, &ger);
+
+ /* Computation of s */
+ for(unsigned int i=0;i<32;i++)
+ sig[i] = r[i];
+
+ get_hram(hram,sig,myPublic.data + 32,sig,96);
+
+ sc25519_from64bytes(&scs, hram);
+ sc25519_from32bytes(&scsk, extsk);
+ sc25519_mul(&scs, &scs, &scsk);
+
+ sc25519_add(&scs, &scs, &sck);
+
+ sc25519_to32bytes(s,&scs); /* cat s */
+ for(unsigned int i=0;i<32;i++)
+ sig[32 + i] = s[i];
+}
+
+void get_hram(unsigned char *hram, const unsigned char *sm, const unsigned char *pk, unsigned char *playground, unsigned long long smlen)
+{
+ unsigned long long i;
+
+ for (i = 0;i < 32;++i) playground[i] = sm[i];
+ for (i = 32;i < 64;++i) playground[i] = pk[i-32];
+ for (i = 64;i < smlen;++i) playground[i] = sm[i];
+
+ //crypto_hash_sha512(hram,playground,smlen);
+ ZeroTier::SHA512::hash(hram,playground,(unsigned int)smlen);
+}
+#endif
+
+extern void ZT_sha512internal(void *digest,const void *data,unsigned int len);
+
+extern void ed25519_amd64_asm_sign(const unsigned char *sk,const unsigned char *pk,const unsigned char *m,const unsigned int mlen,unsigned char *sig)
+{
+ unsigned char az[64];
+ unsigned char nonce[64];
+ unsigned char hram[64];
+ sc25519 sck, scs, scsk;
+ ge25519 ger;
+ unsigned char digest[64];
+ unsigned int i;
+
+ ZT_sha512internal(digest,m,mlen);
+
+ ZT_sha512internal(az,sk,32);
+ az[0] &= 248;
+ az[31] &= 127;
+ az[31] |= 64;
+
+ for(i=0;i<32;i++)
+ sig[32 + i] = az[32 + i];
+ for(i=0;i<32;i++)
+ sig[64 + i] = digest[i];
+
+ ZT_sha512internal(nonce,sig + 32,64);
+
+ sc25519_from64bytes(&sck, nonce);
+ ge25519_scalarmult_base(&ger, &sck);
+ ge25519_pack(sig, &ger);
+
+ memmove(sig + 32,pk,32);
+
+ ZT_sha512internal(hram,sig,96);
+
+ sc25519_from64bytes(&scs, hram);
+ sc25519_from32bytes(&scsk, az);
+ sc25519_mul(&scs, &scs, &scsk);
+ sc25519_add(&scs, &scs, &sck);
+
+ sc25519_to32bytes(sig + 32,&scs);
+}
diff --git a/ext/ed25519-amd64-asm/ull4_mul.s b/ext/ed25519-amd64-asm/ull4_mul.s
new file mode 100644
index 00000000..9f7b4fa2
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ull4_mul.s
@@ -0,0 +1,716 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 xp
+
+# qhasm: int64 yp
+
+# qhasm: input rp
+
+# qhasm: input xp
+
+# qhasm: input yp
+
+# qhasm: int64 r0
+
+# qhasm: int64 r1
+
+# qhasm: int64 r2
+
+# qhasm: int64 r3
+
+# qhasm: int64 r4
+
+# qhasm: int64 r5
+
+# qhasm: int64 r6
+
+# qhasm: int64 r7
+
+# qhasm: int64 c
+
+# qhasm: int64 zero
+
+# qhasm: int64 rax
+
+# qhasm: int64 rdx
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_ull4_mul
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_ull4_mul
+.globl crypto_sign_ed25519_amd64_64_ull4_mul
+_crypto_sign_ed25519_amd64_64_ull4_mul:
+crypto_sign_ed25519_amd64_64_ull4_mul:
+mov %rsp,%r11
+and $31,%r11
+add $64,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: yp = yp
+# asm 1: mov <yp=int64#3,>yp=int64#4
+# asm 2: mov <yp=%rdx,>yp=%rcx
+mov %rdx,%rcx
+
+# qhasm: r4 = 0
+# asm 1: mov $0,>r4=int64#5
+# asm 2: mov $0,>r4=%r8
+mov $0,%r8
+
+# qhasm: r5 = 0
+# asm 1: mov $0,>r5=int64#6
+# asm 2: mov $0,>r5=%r9
+mov $0,%r9
+
+# qhasm: r6 = 0
+# asm 1: mov $0,>r6=int64#8
+# asm 2: mov $0,>r6=%r10
+mov $0,%r10
+
+# qhasm: r7 = 0
+# asm 1: mov $0,>r7=int64#9
+# asm 2: mov $0,>r7=%r11
+mov $0,%r11
+
+# qhasm: zero = 0
+# asm 1: mov $0,>zero=int64#10
+# asm 2: mov $0,>zero=%r12
+mov $0,%r12
+
+# qhasm: rax = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#2),>rax=int64#7
+# asm 2: movq 0(<xp=%rsi),>rax=%rax
+movq 0(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 0)
+# asm 1: mulq 0(<yp=int64#4)
+# asm 2: mulq 0(<yp=%rcx)
+mulq 0(%rcx)
+
+# qhasm: r0 = rax
+# asm 1: mov <rax=int64#7,>r0=int64#11
+# asm 2: mov <rax=%rax,>r0=%r13
+mov %rax,%r13
+
+# qhasm: c = rdx
+# asm 1: mov <rdx=int64#3,>c=int64#12
+# asm 2: mov <rdx=%rdx,>c=%r14
+mov %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#2),>rax=int64#7
+# asm 2: movq 0(<xp=%rsi),>rax=%rax
+movq 0(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 8)
+# asm 1: mulq 8(<yp=int64#4)
+# asm 2: mulq 8(<yp=%rcx)
+mulq 8(%rcx)
+
+# qhasm: r1 = rax
+# asm 1: mov <rax=int64#7,>r1=int64#13
+# asm 2: mov <rax=%rax,>r1=%r15
+mov %rax,%r15
+
+# qhasm: carry? r1 += c
+# asm 1: add <c=int64#12,<r1=int64#13
+# asm 2: add <c=%r14,<r1=%r15
+add %r14,%r15
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#12
+# asm 2: mov $0,>c=%r14
+mov $0,%r14
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#12
+# asm 2: adc <rdx=%rdx,<c=%r14
+adc %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#2),>rax=int64#7
+# asm 2: movq 0(<xp=%rsi),>rax=%rax
+movq 0(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 16)
+# asm 1: mulq 16(<yp=int64#4)
+# asm 2: mulq 16(<yp=%rcx)
+mulq 16(%rcx)
+
+# qhasm: r2 = rax
+# asm 1: mov <rax=int64#7,>r2=int64#14
+# asm 2: mov <rax=%rax,>r2=%rbx
+mov %rax,%rbx
+
+# qhasm: carry? r2 += c
+# asm 1: add <c=int64#12,<r2=int64#14
+# asm 2: add <c=%r14,<r2=%rbx
+add %r14,%rbx
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#12
+# asm 2: mov $0,>c=%r14
+mov $0,%r14
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#12
+# asm 2: adc <rdx=%rdx,<c=%r14
+adc %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#2),>rax=int64#7
+# asm 2: movq 0(<xp=%rsi),>rax=%rax
+movq 0(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 24)
+# asm 1: mulq 24(<yp=int64#4)
+# asm 2: mulq 24(<yp=%rcx)
+mulq 24(%rcx)
+
+# qhasm: r3 = rax
+# asm 1: mov <rax=int64#7,>r3=int64#15
+# asm 2: mov <rax=%rax,>r3=%rbp
+mov %rax,%rbp
+
+# qhasm: carry? r3 += c
+# asm 1: add <c=int64#12,<r3=int64#15
+# asm 2: add <c=%r14,<r3=%rbp
+add %r14,%rbp
+
+# qhasm: r4 += rdx + carry
+# asm 1: adc <rdx=int64#3,<r4=int64#5
+# asm 2: adc <rdx=%rdx,<r4=%r8
+adc %rdx,%r8
+
+# qhasm: rax = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>rax=int64#7
+# asm 2: movq 8(<xp=%rsi),>rax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 0)
+# asm 1: mulq 0(<yp=int64#4)
+# asm 2: mulq 0(<yp=%rcx)
+mulq 0(%rcx)
+
+# qhasm: carry? r1 += rax
+# asm 1: add <rax=int64#7,<r1=int64#13
+# asm 2: add <rax=%rax,<r1=%r15
+add %rax,%r15
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#12
+# asm 2: mov $0,>c=%r14
+mov $0,%r14
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#12
+# asm 2: adc <rdx=%rdx,<c=%r14
+adc %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>rax=int64#7
+# asm 2: movq 8(<xp=%rsi),>rax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 8)
+# asm 1: mulq 8(<yp=int64#4)
+# asm 2: mulq 8(<yp=%rcx)
+mulq 8(%rcx)
+
+# qhasm: carry? r2 += rax
+# asm 1: add <rax=int64#7,<r2=int64#14
+# asm 2: add <rax=%rax,<r2=%rbx
+add %rax,%rbx
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#10,<rdx=int64#3
+# asm 2: adc <zero=%r12,<rdx=%rdx
+adc %r12,%rdx
+
+# qhasm: carry? r2 += c
+# asm 1: add <c=int64#12,<r2=int64#14
+# asm 2: add <c=%r14,<r2=%rbx
+add %r14,%rbx
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#12
+# asm 2: mov $0,>c=%r14
+mov $0,%r14
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#12
+# asm 2: adc <rdx=%rdx,<c=%r14
+adc %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>rax=int64#7
+# asm 2: movq 8(<xp=%rsi),>rax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 16)
+# asm 1: mulq 16(<yp=int64#4)
+# asm 2: mulq 16(<yp=%rcx)
+mulq 16(%rcx)
+
+# qhasm: carry? r3 += rax
+# asm 1: add <rax=int64#7,<r3=int64#15
+# asm 2: add <rax=%rax,<r3=%rbp
+add %rax,%rbp
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#10,<rdx=int64#3
+# asm 2: adc <zero=%r12,<rdx=%rdx
+adc %r12,%rdx
+
+# qhasm: carry? r3 += c
+# asm 1: add <c=int64#12,<r3=int64#15
+# asm 2: add <c=%r14,<r3=%rbp
+add %r14,%rbp
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#12
+# asm 2: mov $0,>c=%r14
+mov $0,%r14
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#12
+# asm 2: adc <rdx=%rdx,<c=%r14
+adc %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>rax=int64#7
+# asm 2: movq 8(<xp=%rsi),>rax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 24)
+# asm 1: mulq 24(<yp=int64#4)
+# asm 2: mulq 24(<yp=%rcx)
+mulq 24(%rcx)
+
+# qhasm: carry? r4 += rax
+# asm 1: add <rax=int64#7,<r4=int64#5
+# asm 2: add <rax=%rax,<r4=%r8
+add %rax,%r8
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#10,<rdx=int64#3
+# asm 2: adc <zero=%r12,<rdx=%rdx
+adc %r12,%rdx
+
+# qhasm: carry? r4 += c
+# asm 1: add <c=int64#12,<r4=int64#5
+# asm 2: add <c=%r14,<r4=%r8
+add %r14,%r8
+
+# qhasm: r5 += rdx + carry
+# asm 1: adc <rdx=int64#3,<r5=int64#6
+# asm 2: adc <rdx=%rdx,<r5=%r9
+adc %rdx,%r9
+
+# qhasm: rax = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>rax=int64#7
+# asm 2: movq 16(<xp=%rsi),>rax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 0)
+# asm 1: mulq 0(<yp=int64#4)
+# asm 2: mulq 0(<yp=%rcx)
+mulq 0(%rcx)
+
+# qhasm: carry? r2 += rax
+# asm 1: add <rax=int64#7,<r2=int64#14
+# asm 2: add <rax=%rax,<r2=%rbx
+add %rax,%rbx
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#12
+# asm 2: mov $0,>c=%r14
+mov $0,%r14
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#12
+# asm 2: adc <rdx=%rdx,<c=%r14
+adc %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>rax=int64#7
+# asm 2: movq 16(<xp=%rsi),>rax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 8)
+# asm 1: mulq 8(<yp=int64#4)
+# asm 2: mulq 8(<yp=%rcx)
+mulq 8(%rcx)
+
+# qhasm: carry? r3 += rax
+# asm 1: add <rax=int64#7,<r3=int64#15
+# asm 2: add <rax=%rax,<r3=%rbp
+add %rax,%rbp
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#10,<rdx=int64#3
+# asm 2: adc <zero=%r12,<rdx=%rdx
+adc %r12,%rdx
+
+# qhasm: carry? r3 += c
+# asm 1: add <c=int64#12,<r3=int64#15
+# asm 2: add <c=%r14,<r3=%rbp
+add %r14,%rbp
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#12
+# asm 2: mov $0,>c=%r14
+mov $0,%r14
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#12
+# asm 2: adc <rdx=%rdx,<c=%r14
+adc %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>rax=int64#7
+# asm 2: movq 16(<xp=%rsi),>rax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 16)
+# asm 1: mulq 16(<yp=int64#4)
+# asm 2: mulq 16(<yp=%rcx)
+mulq 16(%rcx)
+
+# qhasm: carry? r4 += rax
+# asm 1: add <rax=int64#7,<r4=int64#5
+# asm 2: add <rax=%rax,<r4=%r8
+add %rax,%r8
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#10,<rdx=int64#3
+# asm 2: adc <zero=%r12,<rdx=%rdx
+adc %r12,%rdx
+
+# qhasm: carry? r4 += c
+# asm 1: add <c=int64#12,<r4=int64#5
+# asm 2: add <c=%r14,<r4=%r8
+add %r14,%r8
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#12
+# asm 2: mov $0,>c=%r14
+mov $0,%r14
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#12
+# asm 2: adc <rdx=%rdx,<c=%r14
+adc %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>rax=int64#7
+# asm 2: movq 16(<xp=%rsi),>rax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 24)
+# asm 1: mulq 24(<yp=int64#4)
+# asm 2: mulq 24(<yp=%rcx)
+mulq 24(%rcx)
+
+# qhasm: carry? r5 += rax
+# asm 1: add <rax=int64#7,<r5=int64#6
+# asm 2: add <rax=%rax,<r5=%r9
+add %rax,%r9
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#10,<rdx=int64#3
+# asm 2: adc <zero=%r12,<rdx=%rdx
+adc %r12,%rdx
+
+# qhasm: carry? r5 += c
+# asm 1: add <c=int64#12,<r5=int64#6
+# asm 2: add <c=%r14,<r5=%r9
+add %r14,%r9
+
+# qhasm: r6 += rdx + carry
+# asm 1: adc <rdx=int64#3,<r6=int64#8
+# asm 2: adc <rdx=%rdx,<r6=%r10
+adc %rdx,%r10
+
+# qhasm: rax = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>rax=int64#7
+# asm 2: movq 24(<xp=%rsi),>rax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 0)
+# asm 1: mulq 0(<yp=int64#4)
+# asm 2: mulq 0(<yp=%rcx)
+mulq 0(%rcx)
+
+# qhasm: carry? r3 += rax
+# asm 1: add <rax=int64#7,<r3=int64#15
+# asm 2: add <rax=%rax,<r3=%rbp
+add %rax,%rbp
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#12
+# asm 2: mov $0,>c=%r14
+mov $0,%r14
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#12
+# asm 2: adc <rdx=%rdx,<c=%r14
+adc %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>rax=int64#7
+# asm 2: movq 24(<xp=%rsi),>rax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 8)
+# asm 1: mulq 8(<yp=int64#4)
+# asm 2: mulq 8(<yp=%rcx)
+mulq 8(%rcx)
+
+# qhasm: carry? r4 += rax
+# asm 1: add <rax=int64#7,<r4=int64#5
+# asm 2: add <rax=%rax,<r4=%r8
+add %rax,%r8
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#10,<rdx=int64#3
+# asm 2: adc <zero=%r12,<rdx=%rdx
+adc %r12,%rdx
+
+# qhasm: carry? r4 += c
+# asm 1: add <c=int64#12,<r4=int64#5
+# asm 2: add <c=%r14,<r4=%r8
+add %r14,%r8
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#12
+# asm 2: mov $0,>c=%r14
+mov $0,%r14
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#12
+# asm 2: adc <rdx=%rdx,<c=%r14
+adc %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>rax=int64#7
+# asm 2: movq 24(<xp=%rsi),>rax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 16)
+# asm 1: mulq 16(<yp=int64#4)
+# asm 2: mulq 16(<yp=%rcx)
+mulq 16(%rcx)
+
+# qhasm: carry? r5 += rax
+# asm 1: add <rax=int64#7,<r5=int64#6
+# asm 2: add <rax=%rax,<r5=%r9
+add %rax,%r9
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#10,<rdx=int64#3
+# asm 2: adc <zero=%r12,<rdx=%rdx
+adc %r12,%rdx
+
+# qhasm: carry? r5 += c
+# asm 1: add <c=int64#12,<r5=int64#6
+# asm 2: add <c=%r14,<r5=%r9
+add %r14,%r9
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#12
+# asm 2: mov $0,>c=%r14
+mov $0,%r14
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#12
+# asm 2: adc <rdx=%rdx,<c=%r14
+adc %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>rax=int64#7
+# asm 2: movq 24(<xp=%rsi),>rax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 24)
+# asm 1: mulq 24(<yp=int64#4)
+# asm 2: mulq 24(<yp=%rcx)
+mulq 24(%rcx)
+
+# qhasm: carry? r6 += rax
+# asm 1: add <rax=int64#7,<r6=int64#8
+# asm 2: add <rax=%rax,<r6=%r10
+add %rax,%r10
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#10,<rdx=int64#3
+# asm 2: adc <zero=%r12,<rdx=%rdx
+adc %r12,%rdx
+
+# qhasm: carry? r6 += c
+# asm 1: add <c=int64#12,<r6=int64#8
+# asm 2: add <c=%r14,<r6=%r10
+add %r14,%r10
+
+# qhasm: r7 += rdx + carry
+# asm 1: adc <rdx=int64#3,<r7=int64#9
+# asm 2: adc <rdx=%rdx,<r7=%r11
+adc %rdx,%r11
+
+# qhasm: *(uint64 *)(rp + 0) = r0
+# asm 1: movq <r0=int64#11,0(<rp=int64#1)
+# asm 2: movq <r0=%r13,0(<rp=%rdi)
+movq %r13,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = r1
+# asm 1: movq <r1=int64#13,8(<rp=int64#1)
+# asm 2: movq <r1=%r15,8(<rp=%rdi)
+movq %r15,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = r2
+# asm 1: movq <r2=int64#14,16(<rp=int64#1)
+# asm 2: movq <r2=%rbx,16(<rp=%rdi)
+movq %rbx,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = r3
+# asm 1: movq <r3=int64#15,24(<rp=int64#1)
+# asm 2: movq <r3=%rbp,24(<rp=%rdi)
+movq %rbp,24(%rdi)
+
+# qhasm: *(uint64 *)(rp + 32) = r4
+# asm 1: movq <r4=int64#5,32(<rp=int64#1)
+# asm 2: movq <r4=%r8,32(<rp=%rdi)
+movq %r8,32(%rdi)
+
+# qhasm: *(uint64 *)(rp + 40) = r5
+# asm 1: movq <r5=int64#6,40(<rp=int64#1)
+# asm 2: movq <r5=%r9,40(<rp=%rdi)
+movq %r9,40(%rdi)
+
+# qhasm: *(uint64 *)(rp + 48) = r6
+# asm 1: movq <r6=int64#8,48(<rp=int64#1)
+# asm 2: movq <r6=%r10,48(<rp=%rdi)
+movq %r10,48(%rdi)
+
+# qhasm: *(uint64 *)(rp + 56) = r7
+# asm 1: movq <r7=int64#9,56(<rp=int64#1)
+# asm 2: movq <r7=%r11,56(<rp=%rdi)
+movq %r11,56(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/installfiles/linux/zerotier-containerized/Dockerfile b/ext/installfiles/linux/zerotier-containerized/Dockerfile
index 678216da..85faace0 100644
--- a/ext/installfiles/linux/zerotier-containerized/Dockerfile
+++ b/ext/installfiles/linux/zerotier-containerized/Dockerfile
@@ -1,7 +1,7 @@
FROM alpine:latest
MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-LABEL version="1.1.14"
+LABEL version="1.2.4"
LABEL description="Containerized ZeroTier One for use on CoreOS or other Docker-only Linux hosts."
# Uncomment to build in container
diff --git a/ext/installfiles/mac/ZeroTier One.pkgproj b/ext/installfiles/mac/ZeroTier One.pkgproj
index 96b1338a..59c22dde 100755
--- a/ext/installfiles/mac/ZeroTier One.pkgproj
+++ b/ext/installfiles/mac/ZeroTier One.pkgproj
@@ -613,6 +613,12 @@
</dict>
<key>PAYLOAD_TYPE</key>
<integer>0</integer>
+ <key>SHOW_INVISIBLE</key>
+ <false/>
+ <key>SPLIT_FORKS</key>
+ <true/>
+ <key>TREAT_MISSING_FILES_AS_WARNING</key>
+ <false/>
<key>VERSION</key>
<integer>3</integer>
</dict>
@@ -641,12 +647,24 @@
<integer>1</integer>
<key>CONCLUSION_ACTION</key>
<integer>0</integer>
+ <key>FOLLOW_SYMBOLIC_LINKS</key>
+ <false/>
<key>IDENTIFIER</key>
<string>com.zerotier.pkg.ZeroTierOne</string>
+ <key>LOCATION</key>
+ <integer>0</integer>
+ <key>NAME</key>
+ <string></string>
<key>OVERWRITE_PERMISSIONS</key>
<false/>
+ <key>PAYLOAD_SIZE</key>
+ <integer>-1</integer>
+ <key>RELOCATABLE</key>
+ <false/>
+ <key>USE_HFS+_COMPRESSION</key>
+ <false/>
<key>VERSION</key>
- <string>1.2.4</string>
+ <string>1.2.6</string>
</dict>
<key>PROJECT_COMMENTS</key>
<dict>
@@ -862,6 +880,8 @@
</array>
<key>NAME</key>
<string>ZeroTier One</string>
+ <key>PAYLOAD_ONLY</key>
+ <false/>
</dict>
</dict>
<key>TYPE</key>
diff --git a/ext/installfiles/mac/launch.sh b/ext/installfiles/mac/launch.sh
index 41c4b9c8..b02a6670 100755
--- a/ext/installfiles/mac/launch.sh
+++ b/ext/installfiles/mac/launch.sh
@@ -1,7 +1,3 @@
#!/bin/bash
-
-zthome="/Library/Application Support/ZeroTier/One"
-export PATH="$zthome:/bin:/usr/bin:/sbin:/usr/sbin"
-
-# Launch ZeroTier One (not as daemon... launchd monitors it)
+export PATH="/Library/Application Support/ZeroTier/One:/bin:/usr/bin:/sbin:/usr/sbin"
exec zerotier-one
diff --git a/ext/installfiles/mac/postinst.sh b/ext/installfiles/mac/postinst.sh
index 2e4f5916..b4ea2ee2 100755
--- a/ext/installfiles/mac/postinst.sh
+++ b/ext/installfiles/mac/postinst.sh
@@ -5,7 +5,7 @@ export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin
OSX_RELEASE=`sw_vers -productVersion | cut -d . -f 1,2`
launchctl unload /Library/LaunchDaemons/com.zerotier.one.plist >>/dev/null 2>&1
-sleep 1
+sleep 0.5
cd "/Library/Application Support/ZeroTier/One"
@@ -23,24 +23,39 @@ if [ "$OSX_RELEASE" = "10.7" ]; then
fi
rm -rf node.log node.log.old root-topology shutdownIfUnreadable autoupdate.log updates.d ui peers.save
+
chown -R 0 tap.kext
chgrp -R 0 tap.kext
+
if [ ! -f authtoken.secret ]; then
- head -c 4096 /dev/urandom | md5 | head -c 24 >authtoken.secret
+ head -c 1024 /dev/urandom | md5 | head -c 24 >authtoken.secret
chown 0 authtoken.secret
chgrp 0 authtoken.secret
chmod 0600 authtoken.secret
fi
+
rm -f zerotier-cli zerotier-idtool
ln -sf zerotier-one zerotier-cli
ln -sf zerotier-one zerotier-idtool
-
mkdir -p /usr/local/bin
cd /usr/local/bin
rm -f zerotier-cli zerotier-idtool
ln -sf "/Library/Application Support/ZeroTier/One/zerotier-one" zerotier-cli
ln -sf "/Library/Application Support/ZeroTier/One/zerotier-one" zerotier-idtool
+cd "/Library/Application Support/ZeroTier/One"
+kextload -r . tap.kext >>/dev/null 2>&1 &
+disown %1
+
launchctl load /Library/LaunchDaemons/com.zerotier.one.plist >>/dev/null 2>&1
+sleep 1
+
+if [ -f /tmp/zt1-gui-restart.tmp ]; then
+ for u in `cat /tmp/zt1-gui-restart.tmp`; do
+ su $u -c '/Applications/ZeroTier\ One.app/Contents/MacOS/ZeroTier\ One &' >>/dev/null 2>&1 &
+ done
+fi
+rm -f /tmp/zt1-gui-restart.tmp
+
exit 0
diff --git a/ext/installfiles/mac/preinst.sh b/ext/installfiles/mac/preinst.sh
index c2cb494b..af2a9320 100755
--- a/ext/installfiles/mac/preinst.sh
+++ b/ext/installfiles/mac/preinst.sh
@@ -2,6 +2,19 @@
export PATH=/bin:/usr/bin:/sbin:/usr/sbin
+rm -f /tmp/zt1-gui-restart.tmp
+for i in `ps axuwww | tr -s ' ' ',' | grep -F '/Applications/ZeroTier,One.app' | grep -F -v grep | cut -d , -f 1,2 | xargs`; do
+ u=`echo $i | cut -d , -f 1`
+ p=`echo $i | cut -d , -f 2`
+ if [ ! -z "$u" -a "0$p" -gt 0 ]; then
+ kill $p >>/dev/null 2>&1
+ sleep 0.2
+ kill -9 $p >>/dev/null 2>&1
+ echo "$u" >>/tmp/zt1-gui-restart.tmp
+ fi
+done
+chmod 0600 /tmp/zt1-gui-restart.tmp
+
if [ -f /Library/LaunchDaemons/com.zerotier.one.plist ]; then
launchctl unload /Library/LaunchDaemons/com.zerotier.one.plist >>/dev/null 2>&1
fi
diff --git a/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x64).aip b/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x64).aip
index db8566cd..7ff1a05e 100644
--- a/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x64).aip
+++ b/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x64).aip
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<DOCUMENT Type="Advanced Installer" CreateVersion="12.0" version="12.5.1" Modules="enterprise" RootPath="." Language="en" Id="{4070644B-EC9F-4962-B14A-770B9E309DC3}">
+<DOCUMENT Type="Advanced Installer" CreateVersion="12.0" version="14.5.2" Modules="enterprise" RootPath="." Language="en" Id="{4070644B-EC9F-4962-B14A-770B9E309DC3}">
<COMPONENT cid="caphyon.advinst.msicomp.ProjectOptionsComponent">
- <ROW Name="HiddenItems" Value="UpdaterComponent;SerValComponent;AutorunComponent;MultipleInstancesComponent;MsiJavaComponent;MsiRegsComponent;MsiExtComponent;MsiAssemblyComponent;MsiServInstComponent;AnalyticsComponent;ActSyncAppComponent;MsiMergeModsComponent;MsiThemeComponent;BackgroundImagesComponent;DictionaryComponent;MsiEnvComponent;ScheduledTasksComponent;CPLAppletComponent;GameUxComponent;FirewallExceptionComponent;UserAccountsComponent;MsiClassComponent;WebApplicationsComponent;MsiOdbcDataSrcComponent;SqlConnectionComponent;SharePointSlnComponent;SilverlightSlnComponent;MsiAppSearchComponent"/>
+ <ROW Name="HiddenItems" Value="UpdaterComponent;SerValComponent;AutorunComponent;MultipleInstancesComponent;MsiJavaComponent;MsiRegsComponent;MsiExtComponent;MsiAssemblyComponent;MsiServInstComponent;AnalyticsComponent;ActSyncAppComponent;MsiMergeModsComponent;MsiThemeComponent;BackgroundImagesComponent;DictionaryComponent;MsiEnvComponent;ScheduledTasksComponent;CPLAppletComponent;GameUxComponent;FirewallExceptionComponent;UserAccountsComponent;MsiClassComponent;WebApplicationsComponent;MsiOdbcDataSrcComponent;SqlConnectionComponent;SharePointSlnComponent;SilverlightSlnComponent;MsiAppSearchComponent;AppXAppDetailsComponent;AppXCapabilitiesComponent;AppXDependenciesComponent;AppXProductDetailsComponent;AppXVisualAssetsComponent;AppXAppDeclarationsComponent;AppXUriRulesComponent"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiPropsComponent">
<ROW Property="AI_BITMAP_DISPLAY_MODE" Value="0"/>
@@ -19,39 +19,56 @@
<ROW Property="ProductLanguage" Value="1033"/>
<ROW Property="ProductName" Value="ZeroTier One Virtual Network Port"/>
<ROW Property="ProductVersion" Value="1.0.0" Type="32"/>
+ <ROW Property="REBOOT" MultiBuildValue="DefaultBuild:ReallySuppress"/>
<ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND"/>
<ROW Property="UpgradeCode" Value="{88AA80DE-14CA-4443-B024-6EC13F3EDDAD}"/>
<ROW Property="WindowsType9X" MultiBuildValue="DefaultBuild:Windows 9x/ME" ValueLocId="-"/>
<ROW Property="WindowsType9XDisplay" MultiBuildValue="DefaultBuild:Windows 9x/ME" ValueLocId="-"/>
- <ROW Property="WindowsTypeNT" MultiBuildValue="DefaultBuild:Windows 2000, Windows 2000 Service Pack 1, Windows 2000 Service Pack 2, Windows 2000 Service Pack 3, Windows 2000 Service Pack 4, Windows XP x86, Windows XP x86 Service Pack 1, Windows XP x86 Service Pack 2, Windows XP x86 Service Pack 3, Windows Server 2003 x86, Windows Server 2003 x86 Service Pack 1, Windows Server 2003 x86 Service Pack 2" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT" MultiBuildValue="DefaultBuild:Windows XP SP3 x86, Windows Server 2003 SP2 x86" ValueLocId="-"/>
<ROW Property="WindowsTypeNT40" MultiBuildValue="DefaultBuild:Windows NT 4.0" ValueLocId="-"/>
<ROW Property="WindowsTypeNT40Display" MultiBuildValue="DefaultBuild:Windows NT 4.0" ValueLocId="-"/>
- <ROW Property="WindowsTypeNT64" MultiBuildValue="DefaultBuild:Windows XP x64, Windows XP x64 Service Pack 1, Windows XP x64 Service Pack 2, Windows Server 2003 x64, Windows Server 2003 x64 Service Pack 1, Windows Server 2003 x64 Service Pack 2" ValueLocId="-"/>
- <ROW Property="WindowsTypeNT64Display" MultiBuildValue="DefaultBuild:Windows XP x64, Windows Server 2003 x64" ValueLocId="-"/>
- <ROW Property="WindowsTypeNTDisplay" MultiBuildValue="DefaultBuild:Windows 2000, Windows XP x86, Windows Server 2003 x86" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT50" MultiBuildValue="DefaultBuild:Windows 2000" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT50Display" MultiBuildValue="DefaultBuild:Windows 2000" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT5X" MultiBuildValue="DefaultBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT5XDisplay" MultiBuildValue="DefaultBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT64" MultiBuildValue="DefaultBuild:Windows XP SP2 x64, Windows Server 2003 SP2 x64" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT64Display" MultiBuildValue="DefaultBuild:Windows XP SP2 x64, Windows Server 2003 SP2 x64" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNTDisplay" MultiBuildValue="DefaultBuild:Windows XP SP3 x86, Windows Server 2003 SP2 x86" ValueLocId="-"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiDirsComponent">
<ROW Directory="APPDIR" Directory_Parent="TARGETDIR" DefaultDir="APPDIR:." IsPseudoRoot="1"/>
<ROW Directory="TARGETDIR" DefaultDir="SourceDir"/>
+ <ROW Directory="TempFolder" Directory_Parent="TARGETDIR" DefaultDir="TEMPFO~1|TempFolder" IsPseudoRoot="1" DirectoryOptions="3"/>
<ROW Directory="zttap300_Dir" Directory_Parent="APPDIR" DefaultDir="zttap300"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiCompsComponent">
<ROW Component="ProductInformation" ComponentId="{FFBF63D7-E03E-4ACA-966A-824C3DF6DEED}" Directory_="APPDIR" Attributes="4" KeyPath="Version"/>
+ <ROW Component="certutil.exe" ComponentId="{675B9939-A983-4E36-A852-54FB122599B1}" Directory_="zttap300_Dir" Attributes="256" KeyPath="certutil.exe"/>
+ <ROW Component="zttap300.cer" ComponentId="{D72CCDC6-1947-4FE1-9DBA-ECE0A024C1A3}" Directory_="zttap300_Dir" Attributes="0" KeyPath="zttap300.cer" Type="0"/>
+ <ROW Component="zttap300.cer_1" ComponentId="{939B64E0-7725-434B-93FE-B1C30940929E}" Directory_="TempFolder" Attributes="0" KeyPath="zttap300.cer_1" Type="0"/>
<ROW Component="zttap300.inf_2" ComponentId="{CB0C4519-617F-4DB5-9BF5-C6CFC6573F9C}" Directory_="zttap300_Dir" Attributes="256" Condition="VersionNT64" KeyPath="zttap300.inf"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiFeatsComponent">
- <ROW Feature="MainFeature" Title="MainFeature" Description="Description" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="ProductInformation"/>
+ <ROW Feature="MainFeature" Title="MainFeature" Description="Description" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="ProductInformation certutil.exe zttap300.cer zttap300.cer_1"/>
<ROW Feature="zttap300" Feature_Parent="MainFeature" Title="zttap300" Description="Description" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="zttap300.inf_2"/>
<ATTRIBUTE name="CurrentFeature" value="MainFeature"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiFilesComponent">
- <ROW File="zttap300.cat" Component_="zttap300.inf_2" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.cat" SelfReg="false"/>
+ <ROW File="certutil.exe" Component_="certutil.exe" FileName="certutil.exe" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\certutil.exe" SelfReg="false" NextFile="zttap300.cer_1" DigSign="true"/>
+ <ROW File="zttap300.cat" Component_="zttap300.inf_2" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.cat" SelfReg="false" NextFile="zttap300.cer"/>
+ <ROW File="zttap300.cer" Component_="zttap300.cer" FileName="zttap300.cer" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\zttap300.cer" SelfReg="false" NextFile="certutil.exe"/>
+ <ROW File="zttap300.cer_1" Component_="zttap300.cer_1" FileName="zttap300.cer" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\zttap300.cer" SelfReg="false"/>
<ROW File="zttap300.inf" Component_="zttap300.inf_2" FileName="zttap300.inf" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.inf" SelfReg="false" NextFile="zttap300.sys"/>
<ROW File="zttap300.sys" Component_="zttap300.inf_2" FileName="zttap300.sys" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.sys" SelfReg="false" NextFile="zttap300.cat"/>
</COMPONENT>
+ <COMPONENT cid="caphyon.advinst.custcomp.AiInstallExecuteSequenceAliasComponent">
+ <ROW AliasRowId="certutil.exe" AliasRowOperation="2" Sequence="4001"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.BootstrOptComponent">
+ <ROW BootstrOptKey="GlobalOptions" GeneralOptions="q" DownloadFolder="[AppDataFolder][|Manufacturer]\[|ProductName]\prerequisites"/>
+ </COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.BuildComponent">
<ROW BuildKey="DefaultBuild" BuildName="DefaultBuild" BuildOrder="1" BuildType="1" PackageFolder="..\..\.." PackageFileName="ZeroTierOne_NDIS6_x64" Languages="en" InstallationType="4" UseLargeSchema="true" MsiPackageType="x64"/>
- <ATTRIBUTE name="CurrentBuild" value="DefaultBuild"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.DictionaryComponent">
<ROW Path="&lt;AI_DICTS&gt;ui.ail"/>
@@ -105,10 +122,13 @@
<ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="PatchWelcomeDlg" Condition="AI_PATCH" Ordering="203"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiCustActComponent">
+ <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH_ORIGINAL" Target="[AI_SETUPEXEPATH]"/>
<ROW Action="AI_DOWNGRADE" Type="19" Target="4010"/>
<ROW Action="AI_DpiContentScale" Type="1" Source="aicustact.dll" Target="DpiContentScale"/>
+ <ROW Action="AI_EnableDebugLog" Type="321" Source="aicustact.dll" Target="EnableDebugLog"/>
<ROW Action="AI_InstallModeCheck" Type="1" Source="aicustact.dll" Target="UpdateInstallMode" WithoutSeq="true"/>
<ROW Action="AI_PREPARE_UPGRADE" Type="65" Source="aicustact.dll" Target="PrepareUpgrade"/>
+ <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH" Target="[AI_SETUPEXEPATH_ORIGINAL]"/>
<ROW Action="AI_RESTORE_LOCATION" Type="65" Source="aicustact.dll" Target="RestoreLocation"/>
<ROW Action="AI_ResolveKnownFolders" Type="1" Source="aicustact.dll" Target="AI_ResolveKnownFolders"/>
<ROW Action="AI_SHOW_LOG" Type="65" Source="aicustact.dll" Target="LaunchLogFile" WithoutSeq="true"/>
@@ -116,9 +136,10 @@
<ROW Action="SET_APPDIR" Type="307" Source="APPDIR" Target="[ProgramFilesFolder][Manufacturer]\[ProductName]" MultiBuildTarget="DefaultBuild:[ProgramFiles64Folder][Manufacturer]\[ProductName]"/>
<ROW Action="SET_SHORTCUTDIR" Type="307" Source="SHORTCUTDIR" Target="[ProgramMenuFolder][ProductName]"/>
<ROW Action="SET_TARGETDIR_TO_APPDIR" Type="51" Source="TARGETDIR" Target="[APPDIR]"/>
+ <ROW Action="certutil.exe" Type="3154" Source="certutil.exe" Target="-addstore -f TrustedPublisher [#zttap300.cer_1]"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiDriverPackagesComponent">
- <ROW InfFileName="zttap300.inf" Flags="6"/>
+ <ROW InfFileName="zttap300.inf" Flags="15"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiIconsComponent">
<ROW Name="ZeroTierIcon.exe" SourcePath="..\..\..\artwork\ZeroTierIcon.ico" Index="0"/>
@@ -128,19 +149,26 @@
<ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
<ROW Action="AI_STORE_LOCATION" Condition="(Not Installed) OR REINSTALL" Sequence="1501"/>
<ROW Action="AI_PREPARE_UPGRADE" Condition="AI_UPGRADE=&quot;No&quot; AND (Not Installed)" Sequence="1399"/>
- <ROW Action="AI_ResolveKnownFolders" Sequence="51"/>
+ <ROW Action="AI_ResolveKnownFolders" Sequence="53"/>
+ <ROW Action="AI_EnableDebugLog" Sequence="51"/>
+ <ROW Action="certutil.exe" Condition="( NOT Installed )" Sequence="6401"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiInstallUISequenceComponent">
<ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
- <ROW Action="AI_ResolveKnownFolders" Sequence="52"/>
- <ROW Action="AI_DpiContentScale" Sequence="51"/>
+ <ROW Action="AI_ResolveKnownFolders" Sequence="53"/>
+ <ROW Action="AI_DpiContentScale" Sequence="52"/>
+ <ROW Action="AI_EnableDebugLog" Sequence="51"/>
+ <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Sequence="99"/>
+ <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Condition="AI_SETUPEXEPATH_ORIGINAL" Sequence="101"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiLaunchConditionsComponent">
- <ROW Condition="( Version9X OR ( NOT VersionNT64 ) OR ( VersionNT64 AND ((VersionNT64 &lt;&gt; 502) OR (((VersionNT64 = 502) AND (ServicePackLevel &gt;= 1)) OR (MsiNTProductType &lt;&gt; 1))) AND ((VersionNT64 &lt;&gt; 502) OR (((VersionNT64 = 502) AND (ServicePackLevel &lt;&gt; 1)) OR (MsiNTProductType &lt;&gt; 1))) AND ((VersionNT64 &lt;&gt; 502) OR (((VersionNT64 = 502) AND (ServicePackLevel &lt;&gt; 2)) OR (MsiNTProductType &lt;&gt; 1))) AND ((VersionNT64 &lt;&gt; 502) OR ((VersionNT64 = 502) AND ((MsiNTProductType = 1) OR (ServicePackLevel &gt;= 1)))) AND ((VersionNT64 &lt;&gt; 502) OR ((VersionNT64 = 502) AND ((MsiNTProductType = 1) OR (ServicePackLevel &lt;&gt; 1)))) AND ((VersionNT64 &lt;&gt; 502) OR ((VersionNT64 = 502) AND ((MsiNTProductType = 1) OR (ServicePackLevel &lt;&gt; 2)))) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT64Display]" DescriptionLocId="AI.LaunchCondition.NoSpecificNT64" IsPredefined="true" Builds="DefaultBuild"/>
- <ROW Condition="( Version9X OR VersionNT64 OR ( VersionNT AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &gt;= 1))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 1))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 2))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 3))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 4))) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &gt;= 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &lt;&gt; 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &lt;&gt; 2))) OR VersionNT64) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &lt;&gt; 3))) OR VersionNT64) AND (((VersionNT &lt;&gt; 502) OR ((VersionNT = 502) AND (ServicePackLevel &gt;= 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 502) OR ((VersionNT = 502) AND (ServicePackLevel &lt;&gt; 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 502) OR ((VersionNT = 502) AND (ServicePackLevel &lt;&gt; 2))) OR VersionNT64) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNTDisplay]" DescriptionLocId="AI.LaunchCondition.NoSpecificNT" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="( Version9X OR ( NOT VersionNT64 ) OR ( VersionNT64 AND ((VersionNT64 &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2) OR (MsiNTProductType &lt;&gt; 1)) AND ((VersionNT64 &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2) OR (MsiNTProductType = 1)) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT64Display]." DescriptionLocId="AI.LaunchCondition.NoSpecificNT64" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="( Version9X OR VersionNT64 OR ( VersionNT AND ((VersionNT &lt;&gt; 501) OR (ServicePackLevel &lt;&gt; 3)) AND ((VersionNT &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2)) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNTDisplay]." DescriptionLocId="AI.LaunchCondition.NoSpecificNT" IsPredefined="true" Builds="DefaultBuild"/>
<ROW Condition="(VersionNT &lt;&gt; 400)" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT40Display]" DescriptionLocId="AI.LaunchCondition.NoNT40" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="(VersionNT &lt;&gt; 500)" Description="[ProductName] cannot be installed on [WindowsTypeNT50Display]." DescriptionLocId="AI.LaunchCondition.NoNT50" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="(VersionNT64 OR ((VersionNT &lt;&gt; 501) OR (ServicePackLevel = 3))) AND ((VersionNT &lt;&gt; 502) OR (ServicePackLevel = 2))" Description="[ProductName] cannot be installed on [WindowsTypeNT5XDisplay]." DescriptionLocId="AI.LaunchCondition.NoNT5X" IsPredefined="true" Builds="DefaultBuild"/>
<ROW Condition="Privileged" Description="[ProductName] requires administrative privileges to install." DescriptionLocId="AI.LaunchCondition.Privileged" IsPredefined="true" Builds="DefaultBuild"/>
- <ROW Condition="VersionNT" Description="[ProductName] cannot be installed on [WindowsType9XDisplay]" DescriptionLocId="AI.LaunchCondition.No9X" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="VersionNT" Description="[ProductName] cannot be installed on [WindowsType9XDisplay]." DescriptionLocId="AI.LaunchCondition.No9X" IsPredefined="true" Builds="DefaultBuild"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiRegsComponent">
<ROW Registry="Path" Root="-1" Key="Software\[Manufacturer]\[ProductName]" Name="Path" Value="[APPDIR]" Component_="ProductInformation"/>
diff --git a/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x86).aip b/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x86).aip
index b83b382c..ef3d58cc 100644
--- a/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x86).aip
+++ b/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x86).aip
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<DOCUMENT Type="Advanced Installer" CreateVersion="12.0" version="12.5.1" Modules="enterprise" RootPath="." Language="en" Id="{4070644B-EC9F-4962-B14A-770B9E309DC3}">
+<DOCUMENT Type="Advanced Installer" CreateVersion="12.0" version="14.5.2" Modules="enterprise" RootPath="." Language="en" Id="{4070644B-EC9F-4962-B14A-770B9E309DC3}">
<COMPONENT cid="caphyon.advinst.msicomp.ProjectOptionsComponent">
- <ROW Name="HiddenItems" Value="UpdaterComponent;SerValComponent;AutorunComponent;MultipleInstancesComponent;MsiJavaComponent;MsiRegsComponent;MsiExtComponent;MsiAssemblyComponent;MsiServInstComponent;AnalyticsComponent;ActSyncAppComponent;MsiMergeModsComponent;MsiThemeComponent;BackgroundImagesComponent;DictionaryComponent;MsiEnvComponent;ScheduledTasksComponent;CPLAppletComponent;GameUxComponent;FirewallExceptionComponent;UserAccountsComponent;MsiClassComponent;WebApplicationsComponent;MsiOdbcDataSrcComponent;SqlConnectionComponent;SharePointSlnComponent;SilverlightSlnComponent;MsiAppSearchComponent"/>
+ <ROW Name="HiddenItems" Value="UpdaterComponent;SerValComponent;AutorunComponent;MultipleInstancesComponent;MsiJavaComponent;MsiRegsComponent;MsiExtComponent;MsiAssemblyComponent;MsiServInstComponent;AnalyticsComponent;ActSyncAppComponent;MsiMergeModsComponent;MsiThemeComponent;BackgroundImagesComponent;DictionaryComponent;MsiEnvComponent;ScheduledTasksComponent;CPLAppletComponent;GameUxComponent;FirewallExceptionComponent;UserAccountsComponent;MsiClassComponent;WebApplicationsComponent;MsiOdbcDataSrcComponent;SqlConnectionComponent;SharePointSlnComponent;SilverlightSlnComponent;MsiAppSearchComponent;AppXAppDetailsComponent;AppXCapabilitiesComponent;AppXDependenciesComponent;AppXProductDetailsComponent;AppXVisualAssetsComponent;AppXAppDeclarationsComponent;AppXUriRulesComponent"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiPropsComponent">
<ROW Property="AI_BITMAP_DISPLAY_MODE" Value="0"/>
@@ -20,39 +20,56 @@
<ROW Property="ProductLanguage" Value="1033"/>
<ROW Property="ProductName" Value="ZeroTier One Virtual Network Port"/>
<ROW Property="ProductVersion" Value="1.0.0" Type="32"/>
+ <ROW Property="REBOOT" MultiBuildValue="DefaultBuild:ReallySuppress"/>
<ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND"/>
<ROW Property="UpgradeCode" Value="{88AA80DE-14CA-4443-B024-6EC13F3EDDAD}"/>
<ROW Property="WindowsType9X" MultiBuildValue="DefaultBuild:Windows 9x/ME" ValueLocId="-"/>
<ROW Property="WindowsType9XDisplay" MultiBuildValue="DefaultBuild:Windows 9x/ME" ValueLocId="-"/>
- <ROW Property="WindowsTypeNT" MultiBuildValue="DefaultBuild:Windows 2000, Windows 2000 Service Pack 1, Windows 2000 Service Pack 2, Windows 2000 Service Pack 3, Windows 2000 Service Pack 4, Windows XP x86, Windows XP x86 Service Pack 1, Windows XP x86 Service Pack 2, Windows XP x86 Service Pack 3, Windows Server 2003 x86, Windows Server 2003 x86 Service Pack 1, Windows Server 2003 x86 Service Pack 2" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT" MultiBuildValue="DefaultBuild:Windows XP SP3 x86, Windows Server 2003 SP2 x86" ValueLocId="-"/>
<ROW Property="WindowsTypeNT40" MultiBuildValue="DefaultBuild:Windows NT 4.0" ValueLocId="-"/>
<ROW Property="WindowsTypeNT40Display" MultiBuildValue="DefaultBuild:Windows NT 4.0" ValueLocId="-"/>
- <ROW Property="WindowsTypeNT64" MultiBuildValue="DefaultBuild:Windows XP x64, Windows XP x64 Service Pack 1, Windows XP x64 Service Pack 2, Windows Server 2003 x64, Windows Server 2003 x64 Service Pack 1, Windows Server 2003 x64 Service Pack 2" ValueLocId="-"/>
- <ROW Property="WindowsTypeNT64Display" MultiBuildValue="DefaultBuild:Windows XP x64, Windows Server 2003 x64" ValueLocId="-"/>
- <ROW Property="WindowsTypeNTDisplay" MultiBuildValue="DefaultBuild:Windows 2000, Windows XP x86, Windows Server 2003 x86" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT50" MultiBuildValue="DefaultBuild:Windows 2000" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT50Display" MultiBuildValue="DefaultBuild:Windows 2000" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT5X" MultiBuildValue="DefaultBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT5XDisplay" MultiBuildValue="DefaultBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT64" MultiBuildValue="DefaultBuild:Windows XP SP2 x64, Windows Server 2003 SP2 x64" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT64Display" MultiBuildValue="DefaultBuild:Windows XP SP2 x64, Windows Server 2003 SP2 x64" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNTDisplay" MultiBuildValue="DefaultBuild:Windows XP SP3 x86, Windows Server 2003 SP2 x86" ValueLocId="-"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiDirsComponent">
<ROW Directory="APPDIR" Directory_Parent="TARGETDIR" DefaultDir="APPDIR:." IsPseudoRoot="1"/>
<ROW Directory="TARGETDIR" DefaultDir="SourceDir"/>
+ <ROW Directory="TempFolder" Directory_Parent="TARGETDIR" DefaultDir="TEMPFO~1|TempFolder" IsPseudoRoot="1" DirectoryOptions="3"/>
<ROW Directory="zttap300_Dir" Directory_Parent="APPDIR" DefaultDir="zttap300"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiCompsComponent">
<ROW Component="ProductInformation" ComponentId="{FFBF63D7-E03E-4ACA-966A-824C3DF6DEED}" Directory_="APPDIR" Attributes="4" KeyPath="Version"/>
+ <ROW Component="certutil.exe" ComponentId="{716192EC-038F-4CB7-9E85-689904DFA5A7}" Directory_="zttap300_Dir" Attributes="256" KeyPath="certutil.exe"/>
+ <ROW Component="zttap300.cer" ComponentId="{38822503-75B2-4D81-8F48-76F776CA921D}" Directory_="zttap300_Dir" Attributes="0" KeyPath="zttap300.cer" Type="0"/>
+ <ROW Component="zttap300.cer_1" ComponentId="{E26B4B86-5B0F-45B7-9B07-4AE5D6FF4F13}" Directory_="TempFolder" Attributes="0" KeyPath="zttap300.cer_1" Type="0"/>
<ROW Component="zttap300.inf_1" ComponentId="{07119034-A51F-4871-B503-09D1340FF248}" Directory_="zttap300_Dir" Attributes="0" Condition="(VersionNT &gt;= 500) AND NOT VersionNT64" KeyPath="zttap300.inf"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiFeatsComponent">
- <ROW Feature="MainFeature" Title="MainFeature" Description="Description" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="ProductInformation"/>
+ <ROW Feature="MainFeature" Title="MainFeature" Description="Description" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="ProductInformation certutil.exe zttap300.cer zttap300.cer_1"/>
<ROW Feature="zttap300" Feature_Parent="MainFeature" Title="zttap300" Description="Description" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="zttap300.inf_1"/>
<ATTRIBUTE name="CurrentFeature" value="MainFeature"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiFilesComponent">
- <ROW File="zttap300.cat" Component_="zttap300.inf_1" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.cat" SelfReg="false"/>
+ <ROW File="certutil.exe" Component_="certutil.exe" FileName="certutil.exe" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\certutil.exe" SelfReg="false" NextFile="zttap300.cer" DigSign="true"/>
+ <ROW File="zttap300.cat" Component_="zttap300.inf_1" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.cat" SelfReg="false" NextFile="certutil.exe"/>
+ <ROW File="zttap300.cer" Component_="zttap300.cer" FileName="zttap300.cer" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\zttap300.cer" SelfReg="false" NextFile="zttap300.cer_1"/>
+ <ROW File="zttap300.cer_1" Component_="zttap300.cer_1" FileName="zttap300.cer" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\zttap300.cer" SelfReg="false"/>
<ROW File="zttap300.inf" Component_="zttap300.inf_1" FileName="zttap300.inf" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.inf" SelfReg="false" NextFile="zttap300.sys"/>
<ROW File="zttap300.sys" Component_="zttap300.inf_1" FileName="zttap300.sys" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.sys" SelfReg="false" NextFile="zttap300.cat"/>
</COMPONENT>
+ <COMPONENT cid="caphyon.advinst.custcomp.AiInstallExecuteSequenceAliasComponent">
+ <ROW AliasRowId="certutil.exe" AliasRowOperation="2" Sequence="4001"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.BootstrOptComponent">
+ <ROW BootstrOptKey="GlobalOptions" GeneralOptions="q" DownloadFolder="[AppDataFolder][|Manufacturer]\[|ProductName]\prerequisites"/>
+ </COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.BuildComponent">
<ROW BuildKey="DefaultBuild" BuildName="DefaultBuild" BuildOrder="1" BuildType="1" PackageFolder="..\..\.." PackageFileName="ZeroTierOne_NDIS6_x86" Languages="en" InstallationType="4" UseLargeSchema="true"/>
- <ATTRIBUTE name="CurrentBuild" value="DefaultBuild"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.DictionaryComponent">
<ROW Path="&lt;AI_DICTS&gt;ui.ail"/>
@@ -106,10 +123,13 @@
<ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="PatchWelcomeDlg" Condition="AI_PATCH" Ordering="203"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiCustActComponent">
+ <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH_ORIGINAL" Target="[AI_SETUPEXEPATH]"/>
<ROW Action="AI_DOWNGRADE" Type="19" Target="4010"/>
<ROW Action="AI_DpiContentScale" Type="1" Source="aicustact.dll" Target="DpiContentScale"/>
+ <ROW Action="AI_EnableDebugLog" Type="321" Source="aicustact.dll" Target="EnableDebugLog"/>
<ROW Action="AI_InstallModeCheck" Type="1" Source="aicustact.dll" Target="UpdateInstallMode" WithoutSeq="true"/>
<ROW Action="AI_PREPARE_UPGRADE" Type="65" Source="aicustact.dll" Target="PrepareUpgrade"/>
+ <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH" Target="[AI_SETUPEXEPATH_ORIGINAL]"/>
<ROW Action="AI_RESTORE_LOCATION" Type="65" Source="aicustact.dll" Target="RestoreLocation"/>
<ROW Action="AI_ResolveKnownFolders" Type="1" Source="aicustact.dll" Target="AI_ResolveKnownFolders"/>
<ROW Action="AI_SHOW_LOG" Type="65" Source="aicustact.dll" Target="LaunchLogFile" WithoutSeq="true"/>
@@ -117,9 +137,10 @@
<ROW Action="SET_APPDIR" Type="307" Source="APPDIR" Target="[ProgramFilesFolder][Manufacturer]\[ProductName]"/>
<ROW Action="SET_SHORTCUTDIR" Type="307" Source="SHORTCUTDIR" Target="[ProgramMenuFolder][ProductName]"/>
<ROW Action="SET_TARGETDIR_TO_APPDIR" Type="51" Source="TARGETDIR" Target="[APPDIR]"/>
+ <ROW Action="certutil.exe" Type="3154" Source="certutil.exe" Target="-addstore -f TrustedPublisher [#zttap300.cer_1]"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiDriverPackagesComponent">
- <ROW InfFileName="zttap300.inf" Flags="6"/>
+ <ROW InfFileName="zttap300.inf" Flags="15"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiIconsComponent">
<ROW Name="ZeroTierIcon.exe" SourcePath="..\..\..\artwork\ZeroTierIcon.ico" Index="0"/>
@@ -129,19 +150,26 @@
<ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
<ROW Action="AI_STORE_LOCATION" Condition="(Not Installed) OR REINSTALL" Sequence="1501"/>
<ROW Action="AI_PREPARE_UPGRADE" Condition="AI_UPGRADE=&quot;No&quot; AND (Not Installed)" Sequence="1399"/>
- <ROW Action="AI_ResolveKnownFolders" Sequence="51"/>
+ <ROW Action="AI_ResolveKnownFolders" Sequence="53"/>
+ <ROW Action="AI_EnableDebugLog" Sequence="51"/>
+ <ROW Action="certutil.exe" Condition="( NOT Installed )" Sequence="6401"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiInstallUISequenceComponent">
<ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
- <ROW Action="AI_ResolveKnownFolders" Sequence="52"/>
- <ROW Action="AI_DpiContentScale" Sequence="51"/>
+ <ROW Action="AI_ResolveKnownFolders" Sequence="53"/>
+ <ROW Action="AI_DpiContentScale" Sequence="52"/>
+ <ROW Action="AI_EnableDebugLog" Sequence="51"/>
+ <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Sequence="99"/>
+ <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Condition="AI_SETUPEXEPATH_ORIGINAL" Sequence="101"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiLaunchConditionsComponent">
- <ROW Condition="( Version9X OR ( NOT VersionNT64 ) OR ( VersionNT64 AND ((VersionNT64 &lt;&gt; 502) OR (((VersionNT64 = 502) AND (ServicePackLevel &gt;= 1)) OR (MsiNTProductType &lt;&gt; 1))) AND ((VersionNT64 &lt;&gt; 502) OR (((VersionNT64 = 502) AND (ServicePackLevel &lt;&gt; 1)) OR (MsiNTProductType &lt;&gt; 1))) AND ((VersionNT64 &lt;&gt; 502) OR (((VersionNT64 = 502) AND (ServicePackLevel &lt;&gt; 2)) OR (MsiNTProductType &lt;&gt; 1))) AND ((VersionNT64 &lt;&gt; 502) OR ((VersionNT64 = 502) AND ((MsiNTProductType = 1) OR (ServicePackLevel &gt;= 1)))) AND ((VersionNT64 &lt;&gt; 502) OR ((VersionNT64 = 502) AND ((MsiNTProductType = 1) OR (ServicePackLevel &lt;&gt; 1)))) AND ((VersionNT64 &lt;&gt; 502) OR ((VersionNT64 = 502) AND ((MsiNTProductType = 1) OR (ServicePackLevel &lt;&gt; 2)))) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT64Display]" DescriptionLocId="AI.LaunchCondition.NoSpecificNT64" IsPredefined="true" Builds="DefaultBuild"/>
- <ROW Condition="( Version9X OR VersionNT64 OR ( VersionNT AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &gt;= 1))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 1))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 2))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 3))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 4))) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &gt;= 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &lt;&gt; 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &lt;&gt; 2))) OR VersionNT64) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &lt;&gt; 3))) OR VersionNT64) AND (((VersionNT &lt;&gt; 502) OR ((VersionNT = 502) AND (ServicePackLevel &gt;= 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 502) OR ((VersionNT = 502) AND (ServicePackLevel &lt;&gt; 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 502) OR ((VersionNT = 502) AND (ServicePackLevel &lt;&gt; 2))) OR VersionNT64) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNTDisplay]" DescriptionLocId="AI.LaunchCondition.NoSpecificNT" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="( Version9X OR ( NOT VersionNT64 ) OR ( VersionNT64 AND ((VersionNT64 &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2) OR (MsiNTProductType &lt;&gt; 1)) AND ((VersionNT64 &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2) OR (MsiNTProductType = 1)) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT64Display]." DescriptionLocId="AI.LaunchCondition.NoSpecificNT64" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="( Version9X OR VersionNT64 OR ( VersionNT AND ((VersionNT &lt;&gt; 501) OR (ServicePackLevel &lt;&gt; 3)) AND ((VersionNT &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2)) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNTDisplay]." DescriptionLocId="AI.LaunchCondition.NoSpecificNT" IsPredefined="true" Builds="DefaultBuild"/>
<ROW Condition="(VersionNT &lt;&gt; 400)" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT40Display]" DescriptionLocId="AI.LaunchCondition.NoNT40" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="(VersionNT &lt;&gt; 500)" Description="[ProductName] cannot be installed on [WindowsTypeNT50Display]." DescriptionLocId="AI.LaunchCondition.NoNT50" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="(VersionNT64 OR ((VersionNT &lt;&gt; 501) OR (ServicePackLevel = 3))) AND ((VersionNT &lt;&gt; 502) OR (ServicePackLevel = 2))" Description="[ProductName] cannot be installed on [WindowsTypeNT5XDisplay]." DescriptionLocId="AI.LaunchCondition.NoNT5X" IsPredefined="true" Builds="DefaultBuild"/>
<ROW Condition="Privileged" Description="[ProductName] requires administrative privileges to install." DescriptionLocId="AI.LaunchCondition.Privileged" IsPredefined="true" Builds="DefaultBuild"/>
- <ROW Condition="VersionNT" Description="[ProductName] cannot be installed on [WindowsType9XDisplay]" DescriptionLocId="AI.LaunchCondition.No9X" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="VersionNT" Description="[ProductName] cannot be installed on [WindowsType9XDisplay]." DescriptionLocId="AI.LaunchCondition.No9X" IsPredefined="true" Builds="DefaultBuild"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiRegsComponent">
<ROW Registry="Path" Root="-1" Key="Software\[Manufacturer]\[ProductName]" Name="Path" Value="[APPDIR]" Component_="ProductInformation"/>
diff --git a/ext/installfiles/windows/ZeroTier One.aip b/ext/installfiles/windows/ZeroTier One.aip
index a63fa2b2..f75edaed 100644
--- a/ext/installfiles/windows/ZeroTier One.aip
+++ b/ext/installfiles/windows/ZeroTier One.aip
@@ -1,462 +1,484 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<DOCUMENT Type="Advanced Installer" CreateVersion="10.9" version="12.5.1" Modules="enterprise" RootPath="." Language="en" Id="{DC564647-6BF0-4550-87F4-89C938D0159C}">
- <COMPONENT cid="caphyon.advinst.msicomp.ProjectOptionsComponent">
- <ROW Name="HiddenItems" Value="UpdaterComponent;SerValComponent;AutorunComponent;MultipleInstancesComponent;MsiJavaComponent;MsiRegsComponent;MsiExtComponent;MsiAssemblyComponent;MsiDriverPackagesComponent;AnalyticsComponent;ActSyncAppComponent;MsiMergeModsComponent;MsiThemeComponent;BackgroundImagesComponent;DictionaryComponent;ScheduledTasksComponent;CPLAppletComponent;GameUxComponent;UserAccountsComponent;MsiClassComponent;WebApplicationsComponent;MsiOdbcDataSrcComponent;SqlConnectionComponent;SharePointSlnComponent;SilverlightSlnComponent;MsiAppSearchComponent"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiPropsComponent">
- <ROW Property="AI_BITMAP_DISPLAY_MODE" Value="0"/>
- <ROW Property="AI_EMBD_MSI_EXTR_PATH" Value="[TempFolder]" ValueLocId="-"/>
- <ROW Property="AI_EXTERNALUIUNINSTALLERNAME" MultiBuildValue="DefaultBuild:aiui"/>
- <ROW Property="AI_FINDEXE_TITLE" Value="Select the installation package for [|ProductName]" ValueLocId="AI.Property.FindExeTitle"/>
- <ROW Property="AI_PREDEF_LCONDS_PROPS" Value="AI_DETECTED_DOTNET_VERSION"/>
- <ROW Property="AI_PRODUCTNAME_ARP" Value="ZeroTier One"/>
- <ROW Property="AI_REQUIRED_DOTNET_DISPLAY" MultiBuildValue="DefaultBuild:4.5" ValueLocId="-"/>
- <ROW Property="AI_REQUIRED_DOTNET_VERSION" MultiBuildValue="DefaultBuild:4.5" ValueLocId="-"/>
- <ROW Property="AI_UNINSTALLER" Value="msiexec.exe"/>
- <ROW Property="ALLUSERS" Value="1"/>
- <ROW Property="ARPCOMMENTS" Value="This installer database contains the logic and data required to install [|ProductName]."/>
- <ROW Property="ARPCONTACT" Value="contact@zerotier.com"/>
- <ROW Property="ARPHELPLINK" Value="https://www.zerotier.com/"/>
- <ROW Property="ARPNOMODIFY" MultiBuildValue="DefaultBuild:1"/>
- <ROW Property="ARPNOREPAIR" Value="1"/>
- <ROW Property="ARPPRODUCTICON" Value="ZeroTierIcon.exe" Type="8"/>
- <ROW Property="ARPSYSTEMCOMPONENT" Value="1"/>
- <ROW Property="ARPURLINFOABOUT" Value="https://www.zerotier.com/"/>
- <ROW Property="ARPURLUPDATEINFO" Value="https://www.zerotier.com/"/>
- <ROW Property="AiFeatIcoZeroTierOne" Value="ZeroTierIcon.exe" Type="8"/>
- <ROW Property="CTRLS" Value="2"/>
- <ROW Property="MSIFASTINSTALL" MultiBuildValue="DefaultBuild:2"/>
- <ROW Property="Manufacturer" Value="ZeroTier, Inc."/>
- <ROW Property="ProductCode" Value="1033:{0AF2DC73-6629-41A1-B0C6-B110582FEE29} " Type="16"/>
- <ROW Property="ProductLanguage" Value="1033"/>
- <ROW Property="ProductName" Value="ZeroTier One"/>
- <ROW Property="ProductVersion" Value="1.2.4" Type="32"/>
- <ROW Property="REBOOT" MultiBuildValue="DefaultBuild:ReallySuppress"/>
- <ROW Property="RUNAPPLICATION" Value="1" Type="4"/>
- <ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND;AI_SETUPEXEPATH;SETUPEXEDIR"/>
- <ROW Property="UpgradeCode" Value="{B0E2A5F3-88B6-4E77-B922-CB4739B4C4C8}"/>
- <ROW Property="WindowsType9X" MultiBuildValue="DefaultBuild:Windows 9x/ME#ExeBuild:Windows 9x/ME" ValueLocId="-"/>
- <ROW Property="WindowsType9XDisplay" MultiBuildValue="DefaultBuild:Windows 9x/ME#ExeBuild:Windows 9x/ME" ValueLocId="-"/>
- <ROW Property="WindowsTypeNT" MultiBuildValue="DefaultBuild:Windows 2000, Windows 2000 Service Pack 1, Windows 2000 Service Pack 2, Windows 2000 Service Pack 3, Windows 2000 Service Pack 4, Windows XP x86, Windows XP x86 Service Pack 1, Windows XP x86 Service Pack 2, Windows XP x86 Service Pack 3, Windows Server 2003 x86, Windows Server 2003 x86 Service Pack 1, Windows Server 2003 x86 Service Pack 2" ValueLocId="-"/>
- <ROW Property="WindowsTypeNT40" MultiBuildValue="DefaultBuild:Windows NT 4.0#ExeBuild:Windows NT 4.0" ValueLocId="-"/>
- <ROW Property="WindowsTypeNT40Display" MultiBuildValue="DefaultBuild:Windows NT 4.0#ExeBuild:Windows NT 4.0" ValueLocId="-"/>
- <ROW Property="WindowsTypeNT64" MultiBuildValue="DefaultBuild:Windows XP x64, Windows XP x64 Service Pack 1, Windows XP x64 Service Pack 2, Windows Server 2003 x64, Windows Server 2003 x64 Service Pack 1, Windows Server 2003 x64 Service Pack 2" ValueLocId="-"/>
- <ROW Property="WindowsTypeNT64Display" MultiBuildValue="DefaultBuild:Windows XP x64, Windows Server 2003 x64" ValueLocId="-"/>
- <ROW Property="WindowsTypeNTDisplay" MultiBuildValue="DefaultBuild:Windows 2000, Windows XP x86, Windows Server 2003 x86" ValueLocId="-"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiDirsComponent">
- <ROW Directory="APPDIR" Directory_Parent="TARGETDIR" DefaultDir="APPDIR:." IsPseudoRoot="1" DirectoryOptions="2"/>
- <ROW Directory="CommonAppDataFolder" Directory_Parent="TARGETDIR" DefaultDir="COMMON~1|CommonAppDataFolder" IsPseudoRoot="1"/>
- <ROW Directory="FontsFolder" Directory_Parent="TARGETDIR" DefaultDir="FONTSF~1|FontsFolder" IsPseudoRoot="1"/>
- <ROW Directory="One_Dir" Directory_Parent="ZeroTier_Dir" DefaultDir="One"/>
- <ROW Directory="ProgramFilesFolder" Directory_Parent="TARGETDIR" DefaultDir="PROGRA~1|ProgramFilesFolder" IsPseudoRoot="1"/>
- <ROW Directory="ProgramMenuFolder" Directory_Parent="TARGETDIR" DefaultDir="PROGRA~2|ProgramMenuFolder" IsPseudoRoot="1"/>
- <ROW Directory="TARGETDIR" DefaultDir="SourceDir"/>
- <ROW Directory="ZeroTier_Dir" Directory_Parent="CommonAppDataFolder" DefaultDir="ZeroTier"/>
- <ROW Directory="networks.d_Dir" Directory_Parent="One_Dir" DefaultDir="networks.d"/>
- <ROW Directory="regid.201001.com.zerotier_Dir" Directory_Parent="CommonAppDataFolder" DefaultDir="REGID2~1.ZER|regid.2010-01.com.zerotier"/>
- <ROW Directory="tapwindows_Dir" Directory_Parent="One_Dir" DefaultDir="TAP-WI~1|tap-windows"/>
- <ROW Directory="x64_Dir" Directory_Parent="tapwindows_Dir" DefaultDir="x64"/>
- <ROW Directory="x86_Dir" Directory_Parent="tapwindows_Dir" DefaultDir="x86"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiCompsComponent">
- <ROW Component="AI_CustomARPName" ComponentId="{BEF49A0A-3D61-4162-8934-88473D8A6F3D}" Directory_="APPDIR" Attributes="4" KeyPath="DisplayName" Options="1"/>
- <ROW Component="AI_DisableModify" ComponentId="{020DCABD-5D56-49B9-AF48-F07F0B55E590}" Directory_="APPDIR" Attributes="4" KeyPath="NoModify" Options="1"/>
- <ROW Component="AI_ExePath" ComponentId="{8E02B36C-7A19-429B-A93E-77A9261AC918}" Directory_="APPDIR" Attributes="4" KeyPath="AI_ExePath"/>
- <ROW Component="Hardcodet.Wpf.TaskbarNotification.dll" ComponentId="{BEA825AF-2555-44AF-BE40-47FFC16DCBA6}" Directory_="APPDIR" Attributes="0" KeyPath="Hardcodet.Wpf.TaskbarNotification.dll"/>
- <ROW Component="Newtonsoft.Json.dll" ComponentId="{0B2F229D-5425-42FB-9E28-F6D25AB2B4B5}" Directory_="APPDIR" Attributes="0" KeyPath="Newtonsoft.Json.dll"/>
- <ROW Component="ProductInformation" ComponentId="{DB078D04-EA8E-4A7C-9001-89BAD932F9D9}" Directory_="APPDIR" Attributes="4" KeyPath="Version"/>
- <ROW Component="ZeroTierOne.exe" ComponentId="{18B51525-77BF-4FD9-9C18-A10D4CFC25BA}" Directory_="APPDIR" Attributes="0" KeyPath="ZeroTierOne.exe"/>
- <ROW Component="copyutil.exe" ComponentId="{9B9E89FB-81CB-4500-978B-11E2FA81B5B4}" Directory_="APPDIR" Attributes="0" KeyPath="copyutil.exe"/>
- <ROW Component="networks.d" ComponentId="{EF54D0DF-889F-41DC-AF5C-4E7F96AB1C8B}" Directory_="networks.d_Dir" Attributes="0"/>
- <ROW Component="regid.201001.com.zerotier" ComponentId="{A39C80FC-6A8F-454F-9052-10DAC3C3B139}" Directory_="regid.201001.com.zerotier_Dir" Attributes="0"/>
- <ROW Component="segoeui.ttf" ComponentId="{9F415308-A118-419F-AD8A-678DEA856B78}" Directory_="FontsFolder" Attributes="144" Type="0"/>
- <ROW Component="zerotierone_x64.exe" ComponentId="{DFCFB72D-B055-4E60-B6D8-81FF585C2183}" Directory_="One_Dir" Attributes="256" Condition="VersionNT64" KeyPath="zerotierone_x64.exe"/>
- <ROW Component="zerotierone_x86.exe" ComponentId="{5D2F3366-4FE1-40A4-A81A-66C49FA11F1C}" Directory_="One_Dir" Attributes="0" Condition="NOT VersionNT64" KeyPath="zerotierone_x86.exe"/>
- <ROW Component="zttap300.cat" ComponentId="{123CD683-FD99-4F0F-8F9B-0222DF409B09}" Directory_="x64_Dir" Attributes="256" Condition="VersionNT64" KeyPath="zttap300.cat_2" Type="0"/>
- <ROW Component="zttap300.cat_1" ComponentId="{9F913E48-095B-4EA3-98DA-EDAB1593F3E3}" Directory_="x86_Dir" Attributes="0" Condition="NOT VersionNT64" KeyPath="zttap300.cat_3" Type="0"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiFeatsComponent">
- <ROW Feature="ZeroTierOne" Title="MainFeature" Description="ZeroTier One" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="AI_CustomARPName AI_DisableModify AI_ExePath Hardcodet.Wpf.TaskbarNotification.dll Newtonsoft.Json.dll ProductInformation ZeroTierOne.exe copyutil.exe networks.d regid.201001.com.zerotier segoeui.ttf zerotierone_x64.exe zerotierone_x86.exe zttap300.cat zttap300.cat_1"/>
- <ATTRIBUTE name="CurrentFeature" value="ZeroTierOne"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiFilesComponent">
- <ROW File="Hardcodet.Wpf.TaskbarNotification.dll" Component_="Hardcodet.Wpf.TaskbarNotification.dll" FileName="HARDCO~1.DLL|Hardcodet.Wpf.TaskbarNotification.dll" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\WinUI\bin\Release\Hardcodet.Wpf.TaskbarNotification.dll" SelfReg="false" NextFile="segoeui.ttf" DigSign="true"/>
- <ROW File="Newtonsoft.Json.dll" Component_="Newtonsoft.Json.dll" FileName="NEWTON~1.DLL|Newtonsoft.Json.dll" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\WinUI\bin\Release\Newtonsoft.Json.dll" SelfReg="false" NextFile="copyutil.exe" DigSign="true"/>
- <ROW File="ZeroTierOne.exe" Component_="ZeroTierOne.exe" FileName="ZEROTI~1.EXE|ZeroTier One.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\WinUI\bin\Release\ZeroTier One.exe" SelfReg="false" NextFile="zttap300.cat_2" DigSign="true"/>
- <ROW File="copyutil.exe" Component_="copyutil.exe" FileName="copyutil.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\copyutil\bin\Release\copyutil.exe" SelfReg="false" NextFile="Hardcodet.Wpf.TaskbarNotification.dll" DigSign="true"/>
- <ROW File="segoeui.ttf" Component_="segoeui.ttf" FileName="segoeui.ttf" Attributes="0" SourcePath="..\..\..\windows\WinUI\Fonts\segoeui.ttf" SelfReg="false" NextFile="segoeuib.ttf"/>
- <ROW File="segoeuib.ttf" Component_="segoeui.ttf" FileName="segoeuib.ttf" Attributes="0" SourcePath="..\..\..\windows\WinUI\Fonts\segoeuib.ttf" SelfReg="false" NextFile="segoeuii.ttf"/>
- <ROW File="segoeuii.ttf" Component_="segoeui.ttf" FileName="segoeuii.ttf" Attributes="0" SourcePath="..\..\..\windows\WinUI\Fonts\segoeuii.ttf" SelfReg="false" NextFile="segoeuiz.ttf"/>
- <ROW File="segoeuiz.ttf" Component_="segoeui.ttf" FileName="segoeuiz.ttf" Attributes="0" SourcePath="..\..\..\windows\WinUI\Fonts\segoeuiz.ttf" SelfReg="false"/>
- <ROW File="zerotierone_x64.exe" Component_="zerotierone_x64.exe" FileName="ZEROTI~2.EXE|zerotier-one_x64.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\Build\x64\Release\zerotier-one_x64.exe" SelfReg="false" NextFile="ZeroTierOne.exe" DigSign="true"/>
- <ROW File="zerotierone_x86.exe" Component_="zerotierone_x86.exe" FileName="ZEROTI~1.EXE|zerotier-one_x86.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\Build\Win32\Release\zerotier-one_x86.exe" SelfReg="false" NextFile="zerotierone_x64.exe" DigSign="true"/>
- <ROW File="zttap300.cat_2" Component_="zttap300.cat" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.cat" SelfReg="false" NextFile="zttap300.sys_2"/>
- <ROW File="zttap300.cat_3" Component_="zttap300.cat_1" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.cat" SelfReg="false" NextFile="zttap300.sys_3"/>
- <ROW File="zttap300.inf" Component_="zttap300.cat" FileName="zttap300.inf" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.inf" SelfReg="false" NextFile="zttap300.cat_3"/>
- <ROW File="zttap300.inf_1" Component_="zttap300.cat_1" FileName="zttap300.inf" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.inf" SelfReg="false" NextFile="Newtonsoft.Json.dll"/>
- <ROW File="zttap300.sys_2" Component_="zttap300.cat" FileName="zttap300.sys" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.sys" SelfReg="false" NextFile="zttap300.inf"/>
- <ROW File="zttap300.sys_3" Component_="zttap300.cat_1" FileName="zttap300.sys" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.sys" SelfReg="false" NextFile="zttap300.inf_1"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.AiPersistentDataComponent">
- <ROW PersistentRow="segoeui.ttf" Type="0" Condition="1"/>
- <ROW PersistentRow="segoeuib.ttf" Type="0" Condition="1"/>
- <ROW PersistentRow="segoeuii.ttf" Type="0" Condition="1"/>
- <ROW PersistentRow="segoeuiz.ttf" Type="0" Condition="1"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.BootstrOptComponent">
- <ROW BootstrOptKey="GlobalOptions" GeneralOptions="qh" DownloadFolder="[AppDataFolder][|Manufacturer]\[|ProductName]\prerequisites" EulaPathLocId="*" IntroTextPathLocId="*"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.BuildComponent">
- <ROW BuildKey="DefaultBuild" BuildName="MSI" BuildOrder="1" BuildType="1" PackageFolder="..\..\.." PackageFileName="ZeroTier One" Languages="en" InstallationType="4" ExtUI="true" UseLargeSchema="true"/>
- <ROW BuildKey="ExeBuild" BuildName="update" BuildOrder="2" BuildType="1" PackageFolder="..\..\.." PackageFileName="ZeroTier One" Languages="en" InstallationType="2" CabsLocation="1" CompressCabs="false" UseLzma="true" LzmaMethod="2" LzmaCompressionLevel="4" PackageType="1" FilesInsideExe="true" ExeIconPath="..\..\..\artwork\ZeroTierIcon.ico" ExtractionFolder="[AppDataFolder][|Manufacturer]\[|ProductName] [|ProductVersion]\install" MsiCmdLine="/qn" UseLargeSchema="true" ExeName="zt1_update_2_1,2_[|ProductVersion]_0"/>
- <ATTRIBUTE name="CurrentBuild" value="DefaultBuild"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.CacheComponent">
- <ATTRIBUTE name="Enable" value="false"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.ChainedPackageComponent">
- <ROW ChainedPackage="ZeroTierOne_NDIS6_x64.msi" Order="1" Options="108" InstallCondition="(VersionNT64)" RemoveCondition="((REMOVE=&quot;ALL&quot;) AND (NOT UPGRADINGPRODUCTCODE) AND (VersionNT64))"/>
- <ROW ChainedPackage="ZeroTierOne_NDIS6_x86.msi" Order="2" Options="108" InstallCondition="(NOT VersionNT64)" RemoveCondition="((REMOVE=&quot;ALL&quot;) AND (NOT UPGRADINGPRODUCTCODE) AND (NOT VersionNT64))"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.ChainedPackageFileComponent">
- <ROW FileId="ZeroTierOne_NDIS6_x64.msi" ChainedPackage="ZeroTierOne_NDIS6_x64.msi" Options="1" TargetPath="ZeroTierOne_NDIS6_x64.msi" Content="..\..\bin\tap-windows-ndis6\x64\ZeroTierOne_NDIS6_x64.msi"/>
- <ROW FileId="ZeroTierOne_NDIS6_x86.msi" ChainedPackage="ZeroTierOne_NDIS6_x86.msi" Options="1" TargetPath="ZeroTierOne_NDIS6_x86.msi" Content="..\..\bin\tap-windows-ndis6\x86\ZeroTierOne_NDIS6_x86.msi"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.DictionaryComponent">
- <ROW Path="&lt;AI_DICTS&gt;ui.ail"/>
- <ROW Path="&lt;AI_DICTS&gt;ui_en.ail"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.DigCertStoreComponent">
- <ROW TimeStampUrl="http://timestamp.verisign.com/scripts/timstamp.dll" SignerDescription="ZeroTier One" DescriptionUrl="https://www.zerotier.com/" SignOptions="7" SignTool="0" UseSha256="1" Thumbprint="7f01c3746df9e6c8235ea2ae38d3cdfeb185728b Subject: ZeroTier, Inc.&#10;Issuer: DigiCert EV Code Signing CA (SHA2)&#10;Valid from 11/30/2016 to 12/05/2019"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.FirewallExceptionComponent">
- <ROW FirewallException="ZeroTierOneControl" DisplayName="ZeroTier One TCP/9993" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="1" Profiles="7" Port="9993" Protocol="TCP"/>
- <ROW FirewallException="ZeroTierOneUDP9993" DisplayName="ZeroTier One UDP/9993" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="1" Profiles="7" Port="9993" Protocol="UDP"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.FragmentComponent">
- <ROW Fragment="CommonUI.aip" Path="&lt;AI_FRAGS&gt;CommonUI.aip"/>
- <ROW Fragment="MaintenanceTypeDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\MaintenanceTypeDlg.aip"/>
- <ROW Fragment="MaintenanceWelcomeDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\MaintenanceWelcomeDlg.aip"/>
- <ROW Fragment="SequenceDialogs.aip" Path="&lt;AI_THEMES&gt;classic\fragments\SequenceDialogs.aip"/>
- <ROW Fragment="Sequences.aip" Path="&lt;AI_FRAGS&gt;Sequences.aip"/>
- <ROW Fragment="StaticUIStrings.aip" Path="&lt;AI_FRAGS&gt;StaticUIStrings.aip"/>
- <ROW Fragment="UI.aip" Path="&lt;AI_THEMES&gt;classic\fragments\UI.aip"/>
- <ROW Fragment="Validation.aip" Path="&lt;AI_FRAGS&gt;Validation.aip"/>
- <ROW Fragment="VerifyRemoveDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\VerifyRemoveDlg.aip"/>
- <ROW Fragment="VerifyRepairDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\VerifyRepairDlg.aip"/>
- <ROW Fragment="WelcomeDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\WelcomeDlg.aip"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiActionTextComponent">
- <ROW Action="AI_AiBackupImmediate" Description="Preparing backup operation" Template="Path: [1]" DescriptionLocId="ActionText.Description.AI_AiBackupImmediate" TemplateLocId="ActionText.Template.AI_AiBackupImmediate"/>
- <ROW Action="AI_AiBackupRollback" Description="Rollback backup" Template="Path: [1]" DescriptionLocId="ActionText.Description.AI_AiBackupRollback" TemplateLocId="ActionText.Template.AI_AiBackupRollback"/>
- <ROW Action="AI_AiRestoreDeferred" Description="Executing restore operation" Template="Path: [1]" DescriptionLocId="ActionText.Description.AI_AiRestoreDeferred" TemplateLocId="ActionText.Template.AI_AiRestoreDeferred"/>
- <ROW Action="AI_AiRestoreRollback" Description="Rollback restore" Template="Path: [1]" DescriptionLocId="ActionText.Description.AI_AiRestoreRollback" TemplateLocId="ActionText.Template.AI_AiRestoreRollback"/>
- <ROW Action="AI_DeleteLzma" Description="Deleting files extracted from archive" DescriptionLocId="ActionText.Description.AI_DeleteLzma" TemplateLocId="-"/>
- <ROW Action="AI_DeleteRLzma" Description="Deleting files extracted from archive" DescriptionLocId="ActionText.Description.AI_DeleteLzma" TemplateLocId="-"/>
- <ROW Action="AI_ExtractFiles" Description="Extracting files from archive" DescriptionLocId="ActionText.Description.AI_ExtractLzma" TemplateLocId="-"/>
- <ROW Action="AI_ExtractLzma" Description="Extracting files from archive" DescriptionLocId="ActionText.Description.AI_ExtractLzma" TemplateLocId="-"/>
- <ROW Action="AI_FwConfig" Description="Executing Windows Firewall configurations" Template="Configuring Windows Firewall rule: &quot;[1]&quot;" DescriptionLocId="ActionText.Description.AI_FwConfig" TemplateLocId="ActionText.Template.AI_FwConfig"/>
- <ROW Action="AI_FwInstall" Description="Generating actions to configure Windows Firewall" DescriptionLocId="ActionText.Description.AI_FwInstall"/>
- <ROW Action="AI_FwRemove" Description="Executing Windows Firewall configurations" Template="Configuring Windows Firewall rule: &quot;[1]&quot;" DescriptionLocId="ActionText.Description.AI_FwRemove" TemplateLocId="ActionText.Template.AI_FwRemove"/>
- <ROW Action="AI_FwRollback" Description="Rolling back Windows Firewall configurations." Template="Rolling back Windows Firewall configurations." DescriptionLocId="ActionText.Description.AI_FwRollback" TemplateLocId="ActionText.Template.AI_FwRollback"/>
- <ROW Action="AI_FwUninstall" Description="Generating actions to configure Windows Firewall" DescriptionLocId="ActionText.Description.AI_FwUninstall"/>
- <ROW Action="AI_TxtUpdaterCommit" Description="Commit text file changes. " Template="Commit text file changes." DescriptionLocId="ActionText.Description.AI_TxtUpdaterCommit" TemplateLocId="ActionText.Template.AI_TxtUpdaterCommit"/>
- <ROW Action="AI_TxtUpdaterConfig" Description="Executing text file updates" Template="Updating text file: &quot;[1]&quot;" DescriptionLocId="ActionText.Description.AI_TxtUpdaterConfig" TemplateLocId="ActionText.Template.AI_TxtUpdaterConfig"/>
- <ROW Action="AI_TxtUpdaterInstall" Description="Generating actions to configure text files updates" DescriptionLocId="ActionText.Description.AI_TxtUpdaterInstall"/>
- <ROW Action="AI_TxtUpdaterRollback" Description="Rolling back text file changes. " Template="Rolling back text file changes." DescriptionLocId="ActionText.Description.AI_TxtUpdaterRollback" TemplateLocId="ActionText.Template.AI_TxtUpdaterRollback"/>
- <ROW Action="AI_XmlCommit" Description="Committing XML file configurations." Template="Committing XML file configurations." DescriptionLocId="ActionText.Description.AI_XmlCommit" TemplateLocId="ActionText.Template.AI_XmlCommit"/>
- <ROW Action="AI_XmlConfig" Description="Executing XML file configurations" Template="Configuring XML file: &quot;[1]&quot;" DescriptionLocId="ActionText.Description.AI_XmlConfig" TemplateLocId="ActionText.Template.AI_XmlConfig"/>
- <ROW Action="AI_XmlInstall" Description="Generating actions to configure XML files" DescriptionLocId="ActionText.Description.AI_XmlInstall"/>
- <ROW Action="AI_XmlRemove" Description="Executing XML file configurations" Template="Configuring XML file: &quot;[1]&quot;" DescriptionLocId="ActionText.Description.AI_XmlRemove" TemplateLocId="ActionText.Template.AI_XmlRemove"/>
- <ROW Action="AI_XmlRollback" Description="Rolling back XML file configurations." Template="Rolling back XML file configurations." DescriptionLocId="ActionText.Description.AI_XmlRollback" TemplateLocId="ActionText.Template.AI_XmlRollback"/>
- <ROW Action="AI_XmlUninstall" Description="Generating actions to configure XML files" DescriptionLocId="ActionText.Description.AI_XmlUninstall"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiAppSearchComponent">
- <ROW Property="AI_SETUPEXEPATH" Signature_="AI_EXE_PATH_CU" Builds="ExeBuild"/>
- <ROW Property="AI_SETUPEXEPATH" Signature_="AI_EXE_PATH_LM" Builds="ExeBuild"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiBinaryComponent">
- <ROW Name="ExternalUICleaner.dll" SourcePath="&lt;AI_CUSTACTS&gt;ExternalUICleaner.dll"/>
- <ROW Name="NetFirewall.dll" SourcePath="&lt;AI_CUSTACTS&gt;NetFirewall.dll"/>
- <ROW Name="Prereq.dll" SourcePath="&lt;AI_CUSTACTS&gt;Prereq.dll"/>
- <ROW Name="ResourceCleaner.dll" SourcePath="&lt;AI_CUSTACTS&gt;ResourceCleaner.dll"/>
- <ROW Name="SoftwareDetector.dll" SourcePath="&lt;AI_CUSTACTS&gt;SoftwareDetector.dll"/>
- <ROW Name="TxtUpdater.dll" SourcePath="&lt;AI_CUSTACTS&gt;TxtUpdater.dll"/>
- <ROW Name="aicustact.dll" SourcePath="&lt;AI_CUSTACTS&gt;aicustact.dll"/>
- <ROW Name="chainersupport.dll" SourcePath="&lt;AI_CUSTACTS&gt;chainersupport.dll"/>
- <ROW Name="lzmaextractor.dll" SourcePath="&lt;AI_CUSTACTS&gt;lzmaextractor.dll"/>
- <ROW Name="msichainer.exe" SourcePath="&lt;AI_CUSTACTS&gt;msichainer.exe"/>
- <ROW Name="xmlCfg.dll" SourcePath="&lt;AI_CUSTACTS&gt;xmlCfg.dll"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiControlComponent">
- <ROW Dialog_="WelcomeDlg" Control="WelcomeDlgDialogInitializer" Type="DialogInitializer" X="0" Y="0" Width="0" Height="0" Attributes="0" Order="-1" TextLocId="-" HelpLocId="-" ExtDataLocId="-"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiControlEventComponent">
- <ROW Dialog_="WelcomeDlg" Control_="Next" Event="EndDialog" Argument="Return" Condition="AI_INSTALL" Ordering="1"/>
- <ROW Dialog_="MaintenanceWelcomeDlg" Control_="Next" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT" Ordering="99"/>
- <ROW Dialog_="CustomizeDlg" Control_="Next" Event="NewDialog" Argument="VerifyReadyDlg" Condition="AI_MAINT" Ordering="101"/>
- <ROW Dialog_="CustomizeDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT" Ordering="1"/>
- <ROW Dialog_="VerifyReadyDlg" Control_="Install" Event="EndDialog" Argument="Return" Condition="AI_MAINT" Ordering="198"/>
- <ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="CustomizeDlg" Condition="AI_MAINT" Ordering="202"/>
- <ROW Dialog_="MaintenanceTypeDlg" Control_="ChangeButton" Event="NewDialog" Argument="CustomizeDlg" Condition="AI_MAINT" Ordering="501"/>
- <ROW Dialog_="MaintenanceTypeDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceWelcomeDlg" Condition="AI_MAINT" Ordering="1"/>
- <ROW Dialog_="MaintenanceTypeDlg" Control_="RemoveButton" Event="NewDialog" Argument="VerifyRemoveDlg" Condition="AI_MAINT AND InstallMode=&quot;Remove&quot;" Ordering="601"/>
- <ROW Dialog_="VerifyRemoveDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT AND InstallMode=&quot;Remove&quot;" Ordering="1"/>
- <ROW Dialog_="MaintenanceTypeDlg" Control_="RepairButton" Event="NewDialog" Argument="VerifyRepairDlg" Condition="AI_MAINT AND InstallMode=&quot;Repair&quot;" Ordering="601"/>
- <ROW Dialog_="VerifyRepairDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT AND InstallMode=&quot;Repair&quot;" Ordering="1"/>
- <ROW Dialog_="VerifyRepairDlg" Control_="Repair" Event="EndDialog" Argument="Return" Condition="AI_MAINT AND InstallMode=&quot;Repair&quot;" Ordering="399" Options="1"/>
- <ROW Dialog_="VerifyRemoveDlg" Control_="Remove" Event="EndDialog" Argument="Return" Condition="AI_MAINT AND InstallMode=&quot;Remove&quot;" Ordering="299" Options="1"/>
- <ROW Dialog_="PatchWelcomeDlg" Control_="Next" Event="NewDialog" Argument="VerifyReadyDlg" Condition="AI_PATCH" Ordering="201"/>
- <ROW Dialog_="VerifyReadyDlg" Control_="Install" Event="EndDialog" Argument="Return" Condition="AI_PATCH" Ordering="199"/>
- <ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="PatchWelcomeDlg" Condition="AI_PATCH" Ordering="203"/>
- <ROW Dialog_="ResumeDlg" Control_="Install" Event="EndDialog" Argument="Return" Condition="AI_RESUME" Ordering="299"/>
- <ROW Dialog_="WelcomeDlg" Control_="Next" Event="SpawnDialog" Argument="OutOfRbDiskDlg" Condition="AI_INSTALL AND OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND (PROMPTROLLBACKCOST=&quot;P&quot; OR NOT PROMPTROLLBACKCOST)" Ordering="2" Options="2"/>
- <ROW Dialog_="WelcomeDlg" Control_="Next" Event="EnableRollback" Argument="False" Condition="AI_INSTALL AND OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST=&quot;D&quot;" Ordering="3" Options="2"/>
- <ROW Dialog_="WelcomeDlg" Control_="Next" Event="SpawnDialog" Argument="OutOfDiskDlg" Condition="AI_INSTALL AND ( (OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 1) OR (OutOfDiskSpace = 1 AND PROMPTROLLBACKCOST=&quot;F&quot;) )" Ordering="4" Options="2"/>
- <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[AI_ButtonText_Next_Orig]" Argument="[ButtonText_Next]" Condition="AI_INSTALL" Ordering="0" Options="2"/>
- <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[ButtonText_Next]" Argument="[[AI_CommitButton]]" Condition="AI_INSTALL" Ordering="1" Options="2"/>
- <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[AI_Text_Next_Orig]" Argument="[Text_Next]" Condition="AI_INSTALL" Ordering="2" Options="2"/>
- <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[Text_Next]" Argument="[Text_Install]" Condition="AI_INSTALL" Ordering="3" Options="2"/>
- <ROW Dialog_="WelcomeDlg" Control_="Back" Event="[ButtonText_Next]" Argument="[AI_ButtonText_Next_Orig]" Condition="AI_INSTALL" Ordering="0" Options="2"/>
- <ROW Dialog_="WelcomeDlg" Control_="Back" Event="[Text_Next]" Argument="[AI_Text_Next_Orig]" Condition="AI_INSTALL" Ordering="1" Options="2"/>
- <ROW Dialog_="FatalError" Control_="Finish" Event="DoAction" Argument="AI_AiBackupCleanup" Condition="1" Ordering="102"/>
- <ROW Dialog_="UserExit" Control_="Finish" Event="DoAction" Argument="AI_AiBackupCleanup" Condition="1" Ordering="101"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiCreateFolderComponent">
- <ROW Directory_="networks.d_Dir" Component_="networks.d" ManualDelete="false"/>
- <ROW Directory_="regid.201001.com.zerotier_Dir" Component_="regid.201001.com.zerotier" ManualDelete="false"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiCustActComponent">
- <ROW Action="AI_AiBackupCleanup" Type="1" Source="ResourceCleaner.dll" Target="OnAiBackupCleanup" WithoutSeq="true"/>
- <ROW Action="AI_AiBackupImmediate" Type="1" Source="ResourceCleaner.dll" Target="OnAiBackupImmediate"/>
- <ROW Action="AI_AiBackupRollback" Type="11521" Source="ResourceCleaner.dll" Target="OnAiBackupRollback"/>
- <ROW Action="AI_AiRestoreDeferred" Type="11265" Source="ResourceCleaner.dll" Target="OnAiRestoreDeferred"/>
- <ROW Action="AI_AiRestoreRollback" Type="11521" Source="ResourceCleaner.dll" Target="OnAiRestoreRollback" WithoutSeq="true"/>
- <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH_ORIGINAL" Target="[AI_SETUPEXEPATH]"/>
- <ROW Action="AI_CommitChainers" Type="11841" Source="chainersupport.dll" Target="CommitChainedPackages" WithoutSeq="true"/>
- <ROW Action="AI_DATA_SETTER" Type="51" Source="CustomActionData" Target="[~]"/>
- <ROW Action="AI_DATA_SETTER_1" Type="51" Source="CustomActionData" Target="[~]"/>
- <ROW Action="AI_DATA_SETTER_2" Type="51" Source="CustomActionData" Target="[~]"/>
- <ROW Action="AI_DATA_SETTER_3" Type="51" Source="CustomActionData" Target="[~]"/>
- <ROW Action="AI_DATA_SETTER_4" Type="51" Source="AI_ExtractFiles" Target="[AI_SETUPEXEPATH]"/>
- <ROW Action="AI_DATA_SETTER_6" Type="51" Source="CustomActionData" Target="ZeroTier One.exe"/>
- <ROW Action="AI_DOWNGRADE" Type="19" Target="4010"/>
- <ROW Action="AI_DeleteCadLzma" Type="51" Source="AI_DeleteLzma" Target="[AI_SETUPEXEPATH]"/>
- <ROW Action="AI_DeleteLzma" Type="1025" Source="lzmaextractor.dll" Target="DeleteLZMAFiles"/>
- <ROW Action="AI_DeleteRCadLzma" Type="51" Source="AI_DeleteRLzma" Target="[AI_SETUPEXEPATH]"/>
- <ROW Action="AI_DeleteRLzma" Type="1281" Source="lzmaextractor.dll" Target="DeleteLZMAFiles"/>
- <ROW Action="AI_DetectSoftware" Type="257" Source="SoftwareDetector.dll" Target="OnDetectSoftware"/>
- <ROW Action="AI_DoRemoveExternalUIStub" Type="3585" Source="ExternalUICleaner.dll" Target="DoRemoveExternalUIStub" WithoutSeq="true"/>
- <ROW Action="AI_DpiContentScale" Type="1" Source="aicustact.dll" Target="DpiContentScale"/>
- <ROW Action="AI_EstimateExtractFiles" Type="1" Source="Prereq.dll" Target="EstimateExtractFiles"/>
- <ROW Action="AI_ExtractCadLzma" Type="51" Source="AI_ExtractLzma" Target="[AI_SETUPEXEPATH]"/>
- <ROW Action="AI_ExtractFiles" Type="1025" Source="Prereq.dll" Target="ExtractSourceFiles" AdditionalSeq="AI_DATA_SETTER_4"/>
- <ROW Action="AI_ExtractLzma" Type="1025" Source="lzmaextractor.dll" Target="ExtractLZMAFiles"/>
- <ROW Action="AI_FindExeLzma" Type="1" Source="lzmaextractor.dll" Target="FindEXE"/>
- <ROW Action="AI_FwConfig" Type="11265" Source="NetFirewall.dll" Target="OnFwConfig" WithoutSeq="true"/>
- <ROW Action="AI_FwInstall" Type="1" Source="NetFirewall.dll" Target="OnFwInstall" AdditionalSeq="AI_DATA_SETTER_2"/>
- <ROW Action="AI_FwRemove" Type="11265" Source="NetFirewall.dll" Target="OnFwRemove" WithoutSeq="true"/>
- <ROW Action="AI_FwRollback" Type="11521" Source="NetFirewall.dll" Target="OnFwRollback" WithoutSeq="true"/>
- <ROW Action="AI_FwUninstall" Type="1" Source="NetFirewall.dll" Target="OnFwUninstall" AdditionalSeq="AI_DATA_SETTER_3"/>
- <ROW Action="AI_GetArpIconPath" Type="1" Source="aicustact.dll" Target="GetArpIconPath"/>
- <ROW Action="AI_InstallModeCheck" Type="1" Source="aicustact.dll" Target="UpdateInstallMode" WithoutSeq="true"/>
- <ROW Action="AI_LaunchApp" Type="1" Source="aicustact.dll" Target="[#ZeroTierOne.exe]"/>
- <ROW Action="AI_PREPARE_UPGRADE" Type="65" Source="aicustact.dll" Target="PrepareUpgrade"/>
- <ROW Action="AI_PrepareChainers" Type="1" Source="chainersupport.dll" Target="PrepareChainedPackages"/>
- <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH" Target="[AI_SETUPEXEPATH_ORIGINAL]"/>
- <ROW Action="AI_RESTORE_LOCATION" Type="65" Source="aicustact.dll" Target="RestoreLocation"/>
- <ROW Action="AI_RemoveExternalUIStub" Type="1" Source="ExternalUICleaner.dll" Target="RemoveExternalUIStub"/>
- <ROW Action="AI_ResolveKnownFolders" Type="1" Source="aicustact.dll" Target="AI_ResolveKnownFolders"/>
- <ROW Action="AI_RollbackChainers" Type="11585" Source="chainersupport.dll" Target="RollbackChainedPackages" WithoutSeq="true"/>
- <ROW Action="AI_SHOW_LOG" Type="65" Source="aicustact.dll" Target="LaunchLogFile" WithoutSeq="true"/>
- <ROW Action="AI_STORE_LOCATION" Type="51" Source="ARPINSTALLLOCATION" Target="[APPDIR]"/>
- <ROW Action="AI_TxtUpdaterCommit" Type="11777" Source="TxtUpdater.dll" Target="OnTxtUpdaterCommit" WithoutSeq="true"/>
- <ROW Action="AI_TxtUpdaterConfig" Type="11265" Source="TxtUpdater.dll" Target="OnTxtUpdaterConfig" WithoutSeq="true"/>
- <ROW Action="AI_TxtUpdaterInstall" Type="1" Source="TxtUpdater.dll" Target="OnTxtUpdaterInstall"/>
- <ROW Action="AI_TxtUpdaterRollback" Type="11521" Source="TxtUpdater.dll" Target="OnTxtUpdaterRollback" WithoutSeq="true"/>
- <ROW Action="AI_XmlCommit" Type="11777" Source="xmlCfg.dll" Target="OnXmlCommit" WithoutSeq="true"/>
- <ROW Action="AI_XmlConfig" Type="11265" Source="xmlCfg.dll" Target="OnXmlConfig" WithoutSeq="true"/>
- <ROW Action="AI_XmlInstall" Type="1" Source="xmlCfg.dll" Target="OnXmlInstall" AdditionalSeq="AI_DATA_SETTER"/>
- <ROW Action="AI_XmlRemove" Type="11265" Source="xmlCfg.dll" Target="OnXmlRemove" WithoutSeq="true"/>
- <ROW Action="AI_XmlRollback" Type="11521" Source="xmlCfg.dll" Target="OnXmlRollback" WithoutSeq="true"/>
- <ROW Action="AI_XmlUninstall" Type="1" Source="xmlCfg.dll" Target="OnXmlUninstall" AdditionalSeq="AI_DATA_SETTER_1"/>
- <ROW Action="SET_APPDIR" Type="307" Source="APPDIR" Target="[ProgramFilesFolder][Manufacturer]\[ProductName]" MultiBuildTarget="DefaultBuild:[ProgramFilesFolder]ZeroTier\One"/>
- <ROW Action="SET_SHORTCUTDIR" Type="307" Source="SHORTCUTDIR" Target="[ProgramMenuFolder][ProductName]" MultiBuildTarget="DefaultBuild:[ProgramMenuFolder]"/>
- <ROW Action="SET_TARGETDIR_TO_APPDIR" Type="51" Source="TARGETDIR" Target="[APPDIR]"/>
- <ROW Action="TapDeviceRemove32" Type="3154" Source="zerotierone_x86.exe" Target="-D"/>
- <ROW Action="TapDeviceRemove64" Type="3154" Source="zerotierone_x64.exe" Target="-D"/>
- <ROW Action="TerminateUI" Type="65" Source="aicustact.dll" Target="StopProcess" Options="1" AdditionalSeq="AI_DATA_SETTER_6"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiEmbeddedChainerComponent">
- <ROW MsiEmbeddedChainer="msichainer.exe" Condition="VersionMsi &gt;= &quot;4.05&quot;" CommandLine="[AI_CHAINER_CMD_LINE]" Source="msichainer.exe" Type="2"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiEnvComponent">
- <ROW Environment="Path" Name="=-*Path" Value="[~];[APPDIR]" Component_="ZeroTierOne.exe"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiFontsComponent">
- <ROW File_="segoeui.ttf"/>
- <ROW File_="segoeuib.ttf"/>
- <ROW File_="segoeuii.ttf"/>
- <ROW File_="segoeuiz.ttf"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiIconsComponent">
- <ROW Name="ZeroTierIcon.exe" SourcePath="..\..\..\artwork\ZeroTierIcon.ico" Index="0"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiInstExSeqComponent">
- <ROW Action="AI_DOWNGRADE" Condition="AI_NEWERPRODUCTFOUND AND (UILevel &lt;&gt; 5)" Sequence="210"/>
- <ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
- <ROW Action="AI_STORE_LOCATION" Condition="(Not Installed) OR REINSTALL" Sequence="1503"/>
- <ROW Action="AI_PREPARE_UPGRADE" Condition="AI_UPGRADE=&quot;No&quot; AND (Not Installed)" Sequence="1399"/>
- <ROW Action="AI_ResolveKnownFolders" Sequence="51"/>
- <ROW Action="AI_XmlInstall" Condition="(REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5103"/>
- <ROW Action="AI_DATA_SETTER" Condition="(REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5102"/>
- <ROW Action="AI_XmlUninstall" Condition="(REMOVE)" Sequence="3102"/>
- <ROW Action="AI_DATA_SETTER_1" Condition="(REMOVE)" Sequence="3101"/>
- <ROW Action="InstallFinalize" Sequence="6597" SeqType="0" MsiKey="InstallFinalize"/>
- <ROW Action="AI_RemoveExternalUIStub" Condition="(REMOVE=&quot;ALL&quot;) AND ((VersionNT &gt; 500) OR((VersionNT = 500) AND (ServicePackLevel &gt;= 4)))" Sequence="1502"/>
- <ROW Action="AI_GetArpIconPath" Sequence="1401"/>
- <ROW Action="TapDeviceRemove32" Condition="( Installed AND ( REMOVE = &quot;ALL&quot; OR AI_INSTALL_MODE = &quot;Remove&quot; ) AND NOT UPGRADINGPRODUCTCODE ) AND ( NOT VersionNT64 )" Sequence="1603"/>
- <ROW Action="TapDeviceRemove64" Condition="( Installed AND ( REMOVE = &quot;ALL&quot; OR AI_INSTALL_MODE = &quot;Remove&quot; ) AND NOT UPGRADINGPRODUCTCODE ) AND ( VersionNT64 )" Sequence="1604"/>
- <ROW Action="AI_PrepareChainers" Condition="VersionMsi &gt;= &quot;4.05&quot;" Sequence="5851"/>
- <ROW Action="AI_FwInstall" Condition="(VersionNT &gt;= 501) AND (REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5802"/>
- <ROW Action="AI_DATA_SETTER_2" Condition="(VersionNT &gt;= 501) AND (REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5801"/>
- <ROW Action="AI_FwUninstall" Condition="(VersionNT &gt;= 501) AND (REMOVE=&quot;ALL&quot;)" Sequence="1702"/>
- <ROW Action="AI_DATA_SETTER_3" Condition="(VersionNT &gt;= 501) AND (REMOVE=&quot;ALL&quot;)" Sequence="1701"/>
- <ROW Action="AI_DetectSoftware" Sequence="102"/>
- <ROW Action="AI_TxtUpdaterInstall" Sequence="5101"/>
- <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Sequence="99" Builds="ExeBuild"/>
- <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Condition="AI_SETUPEXEPATH_ORIGINAL" Sequence="101" Builds="ExeBuild"/>
- <ROW Action="AI_DeleteCadLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="199" Builds="ExeBuild"/>
- <ROW Action="AI_DeleteRCadLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="198" Builds="ExeBuild"/>
- <ROW Action="AI_ExtractCadLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="197" Builds="ExeBuild"/>
- <ROW Action="AI_FindExeLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="196" Builds="ExeBuild"/>
- <ROW Action="AI_ExtractLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="1549" Builds="ExeBuild"/>
- <ROW Action="AI_DeleteRLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="1548" Builds="ExeBuild"/>
- <ROW Action="AI_DeleteLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="6595" Builds="ExeBuild"/>
- <ROW Action="AI_ExtractFiles" Sequence="3998" Builds="ExeBuild"/>
- <ROW Action="AI_DATA_SETTER_4" Sequence="3997"/>
- <ROW Action="AI_EstimateExtractFiles" Sequence="3999" Builds="ExeBuild"/>
- <ROW Action="TerminateUI" Sequence="1602"/>
- <ROW Action="AI_DATA_SETTER_6" Sequence="1601"/>
- <ROW Action="AI_AiBackupImmediate" Sequence="1001"/>
- <ROW Action="AI_AiBackupRollback" Sequence="1501"/>
- <ROW Action="AI_AiRestoreDeferred" Sequence="6596"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiInstallUISequenceComponent">
- <ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
- <ROW Action="AI_ResolveKnownFolders" Sequence="52"/>
- <ROW Action="AI_DpiContentScale" Sequence="51"/>
- <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Sequence="99"/>
- <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Condition="AI_SETUPEXEPATH_ORIGINAL" Sequence="102"/>
- <ROW Action="ExecuteAction" Sequence="1299" SeqType="0" MsiKey="ExecuteAction"/>
- <ROW Action="AI_DetectSoftware" Sequence="101"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiLaunchConditionsComponent">
- <ROW Condition="( Version9X OR ( NOT VersionNT64 ) OR ( VersionNT64 AND ((VersionNT64 &lt;&gt; 502) OR (((VersionNT64 = 502) AND (ServicePackLevel &gt;= 1)) OR (MsiNTProductType &lt;&gt; 1))) AND ((VersionNT64 &lt;&gt; 502) OR (((VersionNT64 = 502) AND (ServicePackLevel &lt;&gt; 1)) OR (MsiNTProductType &lt;&gt; 1))) AND ((VersionNT64 &lt;&gt; 502) OR (((VersionNT64 = 502) AND (ServicePackLevel &lt;&gt; 2)) OR (MsiNTProductType &lt;&gt; 1))) AND ((VersionNT64 &lt;&gt; 502) OR ((VersionNT64 = 502) AND ((MsiNTProductType = 1) OR (ServicePackLevel &gt;= 1)))) AND ((VersionNT64 &lt;&gt; 502) OR ((VersionNT64 = 502) AND ((MsiNTProductType = 1) OR (ServicePackLevel &lt;&gt; 1)))) AND ((VersionNT64 &lt;&gt; 502) OR ((VersionNT64 = 502) AND ((MsiNTProductType = 1) OR (ServicePackLevel &lt;&gt; 2)))) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT64Display]" DescriptionLocId="AI.LaunchCondition.NoSpecificNT64" IsPredefined="true" Builds="DefaultBuild"/>
- <ROW Condition="( Version9X OR VersionNT64 OR ( VersionNT AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &gt;= 1))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 1))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 2))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 3))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 4))) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &gt;= 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &lt;&gt; 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &lt;&gt; 2))) OR VersionNT64) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &lt;&gt; 3))) OR VersionNT64) AND (((VersionNT &lt;&gt; 502) OR ((VersionNT = 502) AND (ServicePackLevel &gt;= 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 502) OR ((VersionNT = 502) AND (ServicePackLevel &lt;&gt; 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 502) OR ((VersionNT = 502) AND (ServicePackLevel &lt;&gt; 2))) OR VersionNT64) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNTDisplay]" DescriptionLocId="AI.LaunchCondition.NoSpecificNT" IsPredefined="true" Builds="DefaultBuild"/>
- <ROW Condition="(VersionNT &lt;&gt; 400)" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT40Display]." DescriptionLocId="AI.LaunchCondition.NoNT40" IsPredefined="true" Builds="DefaultBuild;ExeBuild"/>
- <ROW Condition="AI_DETECTED_DOTNET_VERSION &gt;= AI_REQUIRED_DOTNET_VERSION" Description="[ProductName] cannot be installed on systems with .NET Framework version lower than [AI_REQUIRED_DOTNET_DISPLAY]." DescriptionLocId="AI.LaunchCondition.DotNET" IsPredefined="true" Builds="DefaultBuild"/>
- <ROW Condition="Privileged" Description="[ProductName] requires administrative privileges to install." DescriptionLocId="AI.LaunchCondition.Privileged" IsPredefined="true" Builds="DefaultBuild"/>
- <ROW Condition="SETUPEXEDIR OR (REMOVE=&quot;ALL&quot;)" Description="This package can only be run from a bootstrapper." DescriptionLocId="AI.LaunchCondition.RequireBootstrapper" IsPredefined="true" Builds="ExeBuild"/>
- <ROW Condition="VersionNT" Description="[ProductName] cannot be installed on [WindowsType9XDisplay]." DescriptionLocId="AI.LaunchCondition.No9X" IsPredefined="true" Builds="DefaultBuild;ExeBuild"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiRegLocatorComponent">
- <ROW Signature_="AI_EXE_PATH_CU" Root="1" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Type="2"/>
- <ROW Signature_="AI_EXE_PATH_LM" Root="2" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Type="2"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiRegsComponent">
- <ROW Registry="AI_ExePath" Root="-1" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Value="[AI_SETUPEXEPATH]" Component_="AI_ExePath"/>
- <ROW Registry="Comments" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="Comments" Value="[ARPCOMMENTS]" Component_="AI_CustomARPName"/>
- <ROW Registry="Contact" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="Contact" Value="[ARPCONTACT]" Component_="AI_CustomARPName"/>
- <ROW Registry="DisplayIcon" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="DisplayIcon" Value="[ARP_ICON_PATH]" Component_="AI_CustomARPName"/>
- <ROW Registry="DisplayName" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="DisplayName" Value="[AI_PRODUCTNAME_ARP]" Component_="AI_CustomARPName"/>
- <ROW Registry="DisplayVersion" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="DisplayVersion" Value="[ProductVersion]" Component_="AI_CustomARPName"/>
- <ROW Registry="HelpLink" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="HelpLink" Value="[ARPHELPLINK]" Component_="AI_CustomARPName"/>
- <ROW Registry="HelpTelephone" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="HelpTelephone" Value="[ARPHELPTELEPHONE]" Component_="AI_CustomARPName"/>
- <ROW Registry="InstallLocation" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="InstallLocation" Value="[APPDIR]" Component_="AI_CustomARPName"/>
- <ROW Registry="ModifyPath" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="ModifyPath" Value="[AI_UNINSTALLER] /I [ProductCode]" Component_="AI_CustomARPName"/>
- <ROW Registry="NoModify" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="NoModify" Value="#1" Component_="AI_DisableModify"/>
- <ROW Registry="NoRepair" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="NoRepair" Value="#1" Component_="AI_CustomARPName"/>
- <ROW Registry="Path" Root="-1" Key="Software\[Manufacturer]\[ProductName]" Name="Path" Value="[APPDIR]" Component_="ProductInformation"/>
- <ROW Registry="Publisher" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="Publisher" Value="[Manufacturer]" Component_="AI_CustomARPName"/>
- <ROW Registry="URLInfoAbout" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="URLInfoAbout" Value="[ARPURLINFOABOUT]" Component_="AI_CustomARPName"/>
- <ROW Registry="URLUpdateInfo" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="URLUpdateInfo" Value="[ARPURLUPDATEINFO]" Component_="AI_CustomARPName"/>
- <ROW Registry="UninstallPath" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="UninstallPath" Value="[AI_UNINSTALLER] /x [ProductCode]" Component_="AI_CustomARPName"/>
- <ROW Registry="UninstallString" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="UninstallString" Value="[AI_UNINSTALLER] /x [ProductCode]" Component_="AI_CustomARPName"/>
- <ROW Registry="Version" Root="-1" Key="Software\[Manufacturer]\[ProductName]" Name="Version" Value="[ProductVersion]" Component_="ProductInformation"/>
- <ROW Registry="VersionMajor" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="VersionMajor" Value="#0" Component_="AI_CustomARPName"/>
- <ROW Registry="VersionMinor" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="VersionMinor" Value="#7" Component_="AI_CustomARPName"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiRemoveFileComponent">
- <ROW FileKey="devcon.log" Component_="ProductInformation" FileName="devcon.log" DirProperty="One_Dir" InstallMode="3"/>
- <ROW FileKey="devcon_x64.exe" Component_="ProductInformation" FileName="devcon_x64.exe" DirProperty="One_Dir" InstallMode="3"/>
- <ROW FileKey="devcon_x86.exe" Component_="ProductInformation" FileName="devcon_x86.exe" DirProperty="One_Dir" InstallMode="3"/>
- <ROW FileKey="node.log" Component_="ProductInformation" FileName="node.log" DirProperty="One_Dir" InstallMode="3"/>
- <ROW FileKey="node.log.old" Component_="ProductInformation" FileName="node.log.old" DirProperty="One_Dir" InstallMode="3"/>
- <ROW FileKey="roottopology" Component_="ProductInformation" FileName="root-topology" DirProperty="One_Dir" InstallMode="3"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiServCtrlComponent">
- <ROW ServiceControl="zerotierone_x64.exe" Name="ZeroTierOneService" Event="163" Wait="1" Component_="zerotierone_x64.exe"/>
- <ROW ServiceControl="zerotierone_x86.exe" Name="ZeroTierOneService" Event="163" Wait="1" Component_="zerotierone_x86.exe"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiServInstComponent">
- <ROW ServiceInstall="zerotierone_x64.exe" Name="ZeroTierOneService" DisplayName="ZeroTier One" ServiceType="16" StartType="2" ErrorControl="32769" Component_="zerotierone_x64.exe" Description="Ethernet Virtualization Service"/>
- <ROW ServiceInstall="zerotierone_x86.exe" Name="ZeroTierOneService" DisplayName="ZeroTier One" ServiceType="16" StartType="2" ErrorControl="32769" Component_="zerotierone_x86.exe" Description="Ethernet Virtualization Service"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiShortsComponent">
- <ROW Shortcut="ZeroTierOne" Directory_="ProgramMenuFolder" Name="ZEROTI~1|ZeroTier One" Component_="ZeroTierOne.exe" Target="[#ZeroTierOne.exe]" Description="Ethernet Virtualization Control Panel" Hotkey="0" Icon_="ZeroTierIcon.exe" IconIndex="0" ShowCmd="1" WkDir="APPDIR"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiThemeComponent">
- <ATTRIBUTE name="UsedTheme" value="classic"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiUpgradeComponent">
- <ROW UpgradeCode="[|UpgradeCode]" VersionMin="0.0.1" VersionMax="[|ProductVersion]" Attributes="257" ActionProperty="OLDPRODUCTS"/>
- <ROW UpgradeCode="[|UpgradeCode]" VersionMin="[|ProductVersion]" Attributes="2" ActionProperty="AI_NEWERPRODUCTFOUND"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.SoftwareIdentificationComponent">
- <ATTRIBUTE name="LocalFile" value="regid.199509.com.example_ProductName.swidtag"/>
- <ATTRIBUTE name="SystemFile" value="regid.199509.com.example_ProductName.swidtag_1"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.TxtUpdateComponent">
- <ROW Name="Append/Create" TxtUpdateSet="zerotiercli.bat" FindPattern="@ECHO OFF&#13;&#10;if [\[][#zerotierone_x64.exe][\]] == [\[][\]] (&#13;&#10;&#9;[#zerotierone_x86.exe] -q %*&#13;&#10;) else (&#13;&#10;&#9;[#zerotierone_x64.exe] -q %*&#13;&#10;)&#13;&#10;" Options="160" Order="0" FileEncoding="0"/>
- <ROW Name="Replace" TxtUpdateSet="zerotiercli.bat" FindPattern="YourFindText" ReplacePattern="YourReplaceText" Options="2" Order="1" FileEncoding="-1"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.TxtUpdateSetComponent">
- <ROW Key="zerotiercli.bat" Component="zerotierone_x64.exe" FileName="zerotier-cli.bat" Directory="APPDIR" Options="17"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.XmlAttributeComponent">
- <ROW XmlAttribute="xmlnsds" XmlElement="swidsoftware_identification_tag" Name="xmlns:ds" Flags="14" Order="0" Value="http://www.w3.org/2000/09/xmldsig#"/>
- <ROW XmlAttribute="xmlnsswid" XmlElement="swidsoftware_identification_tag" Name="xmlns:swid" Flags="14" Order="1" Value="http://standards.iso.org/iso/19770/-2/2008/schema.xsd"/>
- <ROW XmlAttribute="xmlnsxsi" XmlElement="swidsoftware_identification_tag" Name="xmlns:xsi" Flags="14" Order="2" Value="http://www.w3.org/2001/XMLSchema-instance"/>
- <ROW XmlAttribute="xsischemaLocation" XmlElement="swidsoftware_identification_tag" Name="xsi:schemaLocation" Flags="14" Order="3" Value="http://standards.iso.org/iso/19770/-2/2008/schema.xsd software_identification_tag.xsd"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.XmlElementComponent">
- <ROW XmlElement="swidbuild" ParentElement="swidnumeric" Name="swid:build" Condition="1" Order="2" Flags="14" Text="4"/>
- <ROW XmlElement="swidentitlement_required_indicator" ParentElement="swidsoftware_identification_tag" Name="swid:entitlement_required_indicator" Condition="1" Order="0" Flags="14" Text="false"/>
- <ROW XmlElement="swidmajor" ParentElement="swidnumeric" Name="swid:major" Condition="1" Order="0" Flags="14" Text="1"/>
- <ROW XmlElement="swidminor" ParentElement="swidnumeric" Name="swid:minor" Condition="1" Order="1" Flags="14" Text="2"/>
- <ROW XmlElement="swidname" ParentElement="swidproduct_version" Name="swid:name" Condition="1" Order="0" Flags="14" Text="[ProductVersion]"/>
- <ROW XmlElement="swidname_1" ParentElement="swidsoftware_creator" Name="swid:name" Condition="1" Order="0" Flags="14" Text="ZeroTier, Inc."/>
- <ROW XmlElement="swidname_2" ParentElement="swidsoftware_licensor" Name="swid:name" Condition="1" Order="0" Flags="14" Text="ZeroTier, Inc."/>
- <ROW XmlElement="swidname_3" ParentElement="swidtag_creator" Name="swid:name" Condition="1" Order="0" Flags="14" Text="ZeroTier, Inc."/>
- <ROW XmlElement="swidnumeric" ParentElement="swidproduct_version" Name="swid:numeric" Condition="1" Order="1" Flags="14"/>
- <ROW XmlElement="swidproduct_title" ParentElement="swidsoftware_identification_tag" Name="swid:product_title" Condition="1" Order="1" Flags="14" Text="[ProductName]"/>
- <ROW XmlElement="swidproduct_version" ParentElement="swidsoftware_identification_tag" Name="swid:product_version" Condition="1" Order="2" Flags="14"/>
- <ROW XmlElement="swidregid" ParentElement="swidsoftware_creator" Name="swid:regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
- <ROW XmlElement="swidregid_1" ParentElement="swidsoftware_licensor" Name="swid:regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
- <ROW XmlElement="swidregid_2" ParentElement="swidtag_creator" Name="swid:regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
- <ROW XmlElement="swidreview" ParentElement="swidnumeric" Name="swid:review" Condition="1" Order="3" Flags="14" Text="0"/>
- <ROW XmlElement="swidsoftware_creator" ParentElement="swidsoftware_identification_tag" Name="swid:software_creator" Condition="1" Order="3" Flags="14"/>
- <ROW XmlElement="swidsoftware_id" ParentElement="swidsoftware_identification_tag" Name="swid:software_id" Condition="1" Order="5" Flags="14"/>
- <ROW XmlElement="swidsoftware_identification_tag" Name="swid:software_identification_tag" Condition="1" Order="0" Flags="14"/>
- <ROW XmlElement="swidsoftware_licensor" ParentElement="swidsoftware_identification_tag" Name="swid:software_licensor" Condition="1" Order="4" Flags="14"/>
- <ROW XmlElement="swidtag_creator" ParentElement="swidsoftware_identification_tag" Name="swid:tag_creator" Condition="1" Order="6" Flags="14"/>
- <ROW XmlElement="swidtag_creator_regid" ParentElement="swidsoftware_id" Name="swid:tag_creator_regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
- <ROW XmlElement="swidunique_id" ParentElement="swidsoftware_id" Name="swid:unique_id" Condition="1" Order="0" Flags="14" Text="ZeroTierOne"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.XmlFileComponent">
- <ROW XmlFile="regid.199509.com.example_ProductName.swidtag" FileName="REGID2~1.SWI|regid.2010-01.com.zerotier_ZeroTierOne.swidtag" DirProperty="APPDIR" Component="ProductInformation" RootElement="swidsoftware_identification_tag" Flags="25" Version="1.0" Encoding="UTF-8" IndentUnits="2"/>
- <ROW XmlFile="regid.199509.com.example_ProductName.swidtag_1" FileName="REGID2~1.SWI|regid.2010-01.com.zerotier_ZeroTierOne.swidtag" DirProperty="regid.201001.com.zerotier_Dir" Component="ProductInformation" RootElement="swidsoftware_identification_tag" Flags="25" Version="1.0" Encoding="UTF-8" IndentUnits="2"/>
- </COMPONENT>
-</DOCUMENT>
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<DOCUMENT Type="Advanced Installer" CreateVersion="10.9" version="14.5.2" Modules="enterprise" RootPath="." Language="en" Id="{DC564647-6BF0-4550-87F4-89C938D0159C}">
+ <COMPONENT cid="caphyon.advinst.msicomp.ProjectOptionsComponent">
+ <ROW Name="HiddenItems" Value="UpdaterComponent;SerValComponent;AutorunComponent;MultipleInstancesComponent;AppXProductDetailsComponent;AppXDependenciesComponent;AppXAppDetailsComponent;AppXVisualAssetsComponent;AppXCapabilitiesComponent;AppXAppDeclarationsComponent;AppXUriRulesComponent;MsiJavaComponent;MsiRegsComponent;MsiExtComponent;MsiAssemblyComponent;AnalyticsComponent;ActSyncAppComponent;MsiMergeModsComponent;MsiThemeComponent;BackgroundImagesComponent;DictionaryComponent;ScheduledTasksComponent;CPLAppletComponent;GameUxComponent;UserAccountsComponent;MsiClassComponent;WebApplicationsComponent;MsiOdbcDataSrcComponent;SqlConnectionComponent;SharePointSlnComponent;SilverlightSlnComponent"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiPropsComponent">
+ <ROW Property="AI_BITMAP_DISPLAY_MODE" Value="0"/>
+ <ROW Property="AI_EMBD_MSI_EXTR_PATH" Value="[TempFolder]" ValueLocId="-"/>
+ <ROW Property="AI_EXTERNALUIUNINSTALLERNAME" MultiBuildValue="DefaultBuild:aiui"/>
+ <ROW Property="AI_FINDEXE_TITLE" Value="Select the installation package for [|ProductName]" ValueLocId="AI.Property.FindExeTitle"/>
+ <ROW Property="AI_PREDEF_LCONDS_PROPS" Value="AI_DETECTED_DOTNET_VERSION"/>
+ <ROW Property="AI_PRODUCTNAME_ARP" Value="ZeroTier One"/>
+ <ROW Property="AI_REQUIRED_DOTNET_DISPLAY" MultiBuildValue="DefaultBuild:4.5" ValueLocId="-"/>
+ <ROW Property="AI_REQUIRED_DOTNET_VERSION" MultiBuildValue="DefaultBuild:4.5" ValueLocId="-"/>
+ <ROW Property="AI_UNINSTALLER" Value="msiexec.exe"/>
+ <ROW Property="ALLUSERS" Value="1"/>
+ <ROW Property="ARPCOMMENTS" Value="This installer database contains the logic and data required to install [|ProductName]."/>
+ <ROW Property="ARPCONTACT" Value="contact@zerotier.com"/>
+ <ROW Property="ARPHELPLINK" Value="https://www.zerotier.com/"/>
+ <ROW Property="ARPNOMODIFY" MultiBuildValue="DefaultBuild:1"/>
+ <ROW Property="ARPNOREPAIR" Value="1" MultiBuildValue="ExeBuild:1"/>
+ <ROW Property="ARPPRODUCTICON" Value="ZeroTierIcon.exe" Type="8"/>
+ <ROW Property="ARPSYSTEMCOMPONENT" Value="1"/>
+ <ROW Property="ARPURLINFOABOUT" Value="https://www.zerotier.com/"/>
+ <ROW Property="ARPURLUPDATEINFO" Value="https://www.zerotier.com/"/>
+ <ROW Property="AiFeatIcoZeroTierOne" Value="ZeroTierIcon.exe" Type="8"/>
+ <ROW Property="CTRLS" Value="2"/>
+ <ROW Property="MSIFASTINSTALL" MultiBuildValue="DefaultBuild:2"/>
+ <ROW Property="Manufacturer" Value="ZeroTier, Inc."/>
+ <ROW Property="ProductCode" Value="1033:{BCCDCFD7-34FA-473E-9E50-20314D1017F6} " Type="16"/>
+ <ROW Property="ProductLanguage" Value="1033"/>
+ <ROW Property="ProductName" Value="ZeroTier One"/>
+ <ROW Property="ProductVersion" Value="1.2.6" Type="32"/>
+ <ROW Property="REBOOT" MultiBuildValue="DefaultBuild:ReallySuppress"/>
+ <ROW Property="RUNAPPLICATION" Value="1" Type="4"/>
+ <ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND;AI_SETUPEXEPATH;SETUPEXEDIR"/>
+ <ROW Property="UpgradeCode" Value="{B0E2A5F3-88B6-4E77-B922-CB4739B4C4C8}"/>
+ <ROW Property="WindowsType9X" MultiBuildValue="DefaultBuild:Windows 9x/ME#ExeBuild:Windows 9x/ME" ValueLocId="-"/>
+ <ROW Property="WindowsType9XDisplay" MultiBuildValue="DefaultBuild:Windows 9x/ME#ExeBuild:Windows 9x/ME" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT" MultiBuildValue="DefaultBuild:Windows XP SP3 x86, Windows Server 2003 SP2 x86" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT40" MultiBuildValue="DefaultBuild:Windows NT 4.0#ExeBuild:Windows NT 4.0" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT40Display" MultiBuildValue="DefaultBuild:Windows NT 4.0#ExeBuild:Windows NT 4.0" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT50" MultiBuildValue="DefaultBuild:Windows 2000#ExeBuild:Windows 2000" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT50Display" MultiBuildValue="DefaultBuild:Windows 2000#ExeBuild:Windows 2000" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT5X" MultiBuildValue="DefaultBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86#ExeBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT5XDisplay" MultiBuildValue="DefaultBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86#ExeBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT64" MultiBuildValue="DefaultBuild:Windows XP SP2 x64, Windows Server 2003 SP2 x64" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT64Display" MultiBuildValue="DefaultBuild:Windows XP SP2 x64, Windows Server 2003 SP2 x64" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNTDisplay" MultiBuildValue="DefaultBuild:Windows XP SP3 x86, Windows Server 2003 SP2 x86" ValueLocId="-"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiDirsComponent">
+ <ROW Directory="APPDIR" Directory_Parent="TARGETDIR" DefaultDir="APPDIR:." IsPseudoRoot="1" DirectoryOptions="2"/>
+ <ROW Directory="CommonAppDataFolder" Directory_Parent="TARGETDIR" DefaultDir="COMMON~1|CommonAppDataFolder" IsPseudoRoot="1"/>
+ <ROW Directory="FontsFolder" Directory_Parent="TARGETDIR" DefaultDir="FONTSF~1|FontsFolder" IsPseudoRoot="1"/>
+ <ROW Directory="One_Dir" Directory_Parent="ZeroTier_Dir" DefaultDir="One"/>
+ <ROW Directory="ProgramFilesFolder" Directory_Parent="TARGETDIR" DefaultDir="PROGRA~1|ProgramFilesFolder" IsPseudoRoot="1"/>
+ <ROW Directory="ProgramMenuFolder" Directory_Parent="TARGETDIR" DefaultDir="PROGRA~2|ProgramMenuFolder" IsPseudoRoot="1"/>
+ <ROW Directory="TARGETDIR" DefaultDir="SourceDir"/>
+ <ROW Directory="ZeroTier_Dir" Directory_Parent="CommonAppDataFolder" DefaultDir="ZeroTier"/>
+ <ROW Directory="networks.d_Dir" Directory_Parent="One_Dir" DefaultDir="networks.d"/>
+ <ROW Directory="regid.201001.com.zerotier_Dir" Directory_Parent="CommonAppDataFolder" DefaultDir="REGID2~1.ZER|regid.2010-01.com.zerotier"/>
+ <ROW Directory="tapwindows_Dir" Directory_Parent="One_Dir" DefaultDir="TAP-WI~1|tap-windows"/>
+ <ROW Directory="x64_Dir" Directory_Parent="tapwindows_Dir" DefaultDir="x64"/>
+ <ROW Directory="x86_Dir" Directory_Parent="tapwindows_Dir" DefaultDir="x86"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiCompsComponent">
+ <ROW Component="AI_CustomARPName" ComponentId="{A4BB4E68-0DD2-4052-BE60-CDCCB9BAC409}" Directory_="APPDIR" Attributes="4" KeyPath="DisplayName" Options="1"/>
+ <ROW Component="AI_DisableModify" ComponentId="{020DCABD-5D56-49B9-AF48-F07F0B55E590}" Directory_="APPDIR" Attributes="4" KeyPath="NoModify" Options="1"/>
+ <ROW Component="AI_ExePath" ComponentId="{8E02B36C-7A19-429B-A93E-77A9261AC918}" Directory_="APPDIR" Attributes="4" KeyPath="AI_ExePath"/>
+ <ROW Component="Hardcodet.Wpf.TaskbarNotification.dll" ComponentId="{BEA825AF-2555-44AF-BE40-47FFC16DCBA6}" Directory_="APPDIR" Attributes="0" KeyPath="Hardcodet.Wpf.TaskbarNotification.dll"/>
+ <ROW Component="Newtonsoft.Json.dll" ComponentId="{0B2F229D-5425-42FB-9E28-F6D25AB2B4B5}" Directory_="APPDIR" Attributes="0" KeyPath="Newtonsoft.Json.dll"/>
+ <ROW Component="ProductInformation" ComponentId="{DB078D04-EA8E-4A7C-9001-89BAD932F9D9}" Directory_="APPDIR" Attributes="4" KeyPath="Version"/>
+ <ROW Component="ZeroTierOne.exe" ComponentId="{18B51525-77BF-4FD9-9C18-A10D4CFC25BA}" Directory_="APPDIR" Attributes="0" KeyPath="ZeroTierOne.exe"/>
+ <ROW Component="copyutil.exe" ComponentId="{9B9E89FB-81CB-4500-978B-11E2FA81B5B4}" Directory_="APPDIR" Attributes="0" KeyPath="copyutil.exe"/>
+ <ROW Component="networks.d" ComponentId="{EF54D0DF-889F-41DC-AF5C-4E7F96AB1C8B}" Directory_="networks.d_Dir" Attributes="0"/>
+ <ROW Component="regid.201001.com.zerotier" ComponentId="{A39C80FC-6A8F-454F-9052-10DAC3C3B139}" Directory_="regid.201001.com.zerotier_Dir" Attributes="0"/>
+ <ROW Component="segoeui.ttf" ComponentId="{9F415308-A118-419F-AD8A-678DEA856B78}" Directory_="FontsFolder" Attributes="144" Type="0"/>
+ <ROW Component="zerotierone_x64.exe" ComponentId="{DFCFB72D-B055-4E60-B6D8-81FF585C2183}" Directory_="One_Dir" Attributes="256" Condition="VersionNT64" KeyPath="zerotierone_x64.exe"/>
+ <ROW Component="zerotierone_x86.exe" ComponentId="{5D2F3366-4FE1-40A4-A81A-66C49FA11F1C}" Directory_="One_Dir" Attributes="0" Condition="NOT VersionNT64" KeyPath="zerotierone_x86.exe"/>
+ <ROW Component="zttap300.cat_1" ComponentId="{9F913E48-095B-4EA3-98DA-EDAB1593F3E3}" Directory_="x86_Dir" Attributes="0" Condition="NOT VersionNT64" KeyPath="zttap300.cat_3" Type="0"/>
+ <ROW Component="zttap300.inf" ComponentId="{D4839F5E-FB94-41CB-9B1B-177A97ADC904}" Directory_="x64_Dir" Attributes="256" Condition="VersionNT64" KeyPath="zttap300.inf"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiFeatsComponent">
+ <ROW Feature="ZeroTierOne" Title="MainFeature" Description="ZeroTier One" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="AI_CustomARPName AI_DisableModify AI_ExePath Hardcodet.Wpf.TaskbarNotification.dll Newtonsoft.Json.dll ProductInformation ZeroTierOne.exe copyutil.exe networks.d regid.201001.com.zerotier segoeui.ttf zerotierone_x64.exe zerotierone_x86.exe zttap300.cat_1"/>
+ <ROW Feature="zttap300" Feature_Parent="ZeroTierOne" Title="zttap300" Description="ZeroTier Virtual Network Port Driver" Display="1" Level="1" Directory_="APPDIR" Attributes="16" Components="zttap300.inf"/>
+ <ATTRIBUTE name="CurrentFeature" value="ZeroTierOne"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiFilesComponent">
+ <ROW File="Hardcodet.Wpf.TaskbarNotification.dll" Component_="Hardcodet.Wpf.TaskbarNotification.dll" FileName="HARDCO~1.DLL|Hardcodet.Wpf.TaskbarNotification.dll" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\WinUI\bin\Release\Hardcodet.Wpf.TaskbarNotification.dll" SelfReg="false" NextFile="segoeui.ttf" DigSign="true"/>
+ <ROW File="Newtonsoft.Json.dll" Component_="Newtonsoft.Json.dll" FileName="NEWTON~1.DLL|Newtonsoft.Json.dll" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\WinUI\bin\Release\Newtonsoft.Json.dll" SelfReg="false" NextFile="copyutil.exe" DigSign="true"/>
+ <ROW File="ZeroTierOne.exe" Component_="ZeroTierOne.exe" FileName="ZEROTI~1.EXE|ZeroTier One.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\WinUI\bin\Release\ZeroTier One.exe" SelfReg="false" NextFile="zttap300.cat_2" DigSign="true"/>
+ <ROW File="copyutil.exe" Component_="copyutil.exe" FileName="copyutil.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\copyutil\bin\Release\copyutil.exe" SelfReg="false" NextFile="Hardcodet.Wpf.TaskbarNotification.dll" DigSign="true"/>
+ <ROW File="segoeui.ttf" Component_="segoeui.ttf" FileName="segoeui.ttf" Attributes="0" SourcePath="..\..\..\windows\WinUI\Fonts\segoeui.ttf" SelfReg="false" NextFile="segoeuib.ttf"/>
+ <ROW File="segoeuib.ttf" Component_="segoeui.ttf" FileName="segoeuib.ttf" Attributes="0" SourcePath="..\..\..\windows\WinUI\Fonts\segoeuib.ttf" SelfReg="false" NextFile="segoeuii.ttf"/>
+ <ROW File="segoeuii.ttf" Component_="segoeui.ttf" FileName="segoeuii.ttf" Attributes="0" SourcePath="..\..\..\windows\WinUI\Fonts\segoeuii.ttf" SelfReg="false" NextFile="segoeuiz.ttf"/>
+ <ROW File="segoeuiz.ttf" Component_="segoeui.ttf" FileName="segoeuiz.ttf" Attributes="0" SourcePath="..\..\..\windows\WinUI\Fonts\segoeuiz.ttf" SelfReg="false"/>
+ <ROW File="zerotierone_x64.exe" Component_="zerotierone_x64.exe" FileName="ZEROTI~2.EXE|zerotier-one_x64.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\Build\x64\Release\zerotier-one_x64.exe" SelfReg="false" NextFile="ZeroTierOne.exe" DigSign="true"/>
+ <ROW File="zerotierone_x86.exe" Component_="zerotierone_x86.exe" FileName="ZEROTI~1.EXE|zerotier-one_x86.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\Build\Win32\Release\zerotier-one_x86.exe" SelfReg="false" NextFile="zerotierone_x64.exe" DigSign="true"/>
+ <ROW File="zttap300.cat_2" Component_="zttap300.inf" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.cat" SelfReg="false" NextFile="zttap300.sys_2"/>
+ <ROW File="zttap300.cat_3" Component_="zttap300.cat_1" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.cat" SelfReg="false" NextFile="zttap300.sys_3"/>
+ <ROW File="zttap300.inf" Component_="zttap300.inf" FileName="zttap300.inf" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.inf" SelfReg="false" NextFile="zttap300.cat_3"/>
+ <ROW File="zttap300.inf_1" Component_="zttap300.cat_1" FileName="zttap300.inf" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.inf" SelfReg="false" NextFile="Newtonsoft.Json.dll"/>
+ <ROW File="zttap300.sys_2" Component_="zttap300.inf" FileName="zttap300.sys" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.sys" SelfReg="false" NextFile="zttap300.inf"/>
+ <ROW File="zttap300.sys_3" Component_="zttap300.cat_1" FileName="zttap300.sys" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.sys" SelfReg="false" NextFile="zttap300.inf_1"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.AiPersistentDataComponent">
+ <ROW PersistentRow="segoeui.ttf" Type="0" Condition="1"/>
+ <ROW PersistentRow="segoeuib.ttf" Type="0" Condition="1"/>
+ <ROW PersistentRow="segoeuii.ttf" Type="0" Condition="1"/>
+ <ROW PersistentRow="segoeuiz.ttf" Type="0" Condition="1"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.BootstrOptComponent">
+ <ROW BootstrOptKey="GlobalOptions" GeneralOptions="qh" DownloadFolder="[AppDataFolder][|Manufacturer]\[|ProductName]\prerequisites"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.BootstrapperUISequenceComponent">
+ <ROW Action="AI_DetectSoftware" Sequence="101"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.BuildComponent">
+ <ROW BuildKey="DefaultBuild" BuildName="MSI" BuildOrder="1" BuildType="1" PackageFolder="..\..\.." PackageFileName="ZeroTier One" Languages="en" InstallationType="4" ExtUI="true" UseLargeSchema="true"/>
+ <ROW BuildKey="ExeBuild" BuildName="update" BuildOrder="2" BuildType="1" PackageFolder="..\..\.." PackageFileName="ZeroTier One" Languages="en" InstallationType="2" CabsLocation="1" CompressCabs="false" UseLzma="true" LzmaMethod="2" LzmaCompressionLevel="4" PackageType="1" FilesInsideExe="true" ExeIconPath="..\..\..\artwork\ZeroTierIcon.ico" ExtractionFolder="[AppDataFolder][|Manufacturer]\[|ProductName] [|ProductVersion]\install" MsiCmdLine="/qn" ExtUI="true" UseLargeSchema="true" ExeName="zt1_update_2_1,2_[|ProductVersion]_0"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.CacheComponent">
+ <ATTRIBUTE name="Enable" value="false"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.ChainedPackageComponent">
+ <ROW ChainedPackage="ZeroTierOne_NDIS6_x64.msi" Order="1" Options="110" InstallCondition="(NOT Installed) AND VersionNT64" MaintenanceCondition="FALSE" RemoveCondition="REMOVE=&quot;ALL&quot; AND VersionNT64"/>
+ <ROW ChainedPackage="ZeroTierOne_NDIS6_x86.msi" Order="2" Options="110" InstallCondition="(NOT Installed) AND (NOT VersionNT64)" MaintenanceCondition="FALSE" RemoveCondition="REMOVE=&quot;ALL&quot; AND (NOT VersionNT64)"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.ChainedPackageFileComponent">
+ <ROW FileId="ZeroTierOne_NDIS6_x64.msi" ChainedPackage="ZeroTierOne_NDIS6_x64.msi" Options="1" TargetPath="ZeroTierOne_NDIS6_x64.msi" Content="..\..\bin\tap-windows-ndis6\x64\ZeroTierOne_NDIS6_x64.msi"/>
+ <ROW FileId="ZeroTierOne_NDIS6_x86.msi" ChainedPackage="ZeroTierOne_NDIS6_x86.msi" Options="1" TargetPath="ZeroTierOne_NDIS6_x86.msi" Content="..\..\bin\tap-windows-ndis6\x86\ZeroTierOne_NDIS6_x86.msi"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.DictionaryComponent">
+ <ROW Path="&lt;AI_DICTS&gt;ui.ail"/>
+ <ROW Path="&lt;AI_DICTS&gt;ui_en.ail"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.DigCertStoreComponent">
+ <ROW TimeStampUrl="http://timestamp.verisign.com/scripts/timstamp.dll" SignerDescription="ZeroTier One" DescriptionUrl="https://www.zerotier.com/" SignOptions="7" SignTool="0" UseSha256="1" Thumbprint="7f01c3746df9e6c8235ea2ae38d3cdfeb185728b Subject: ZeroTier, Inc.&#10;Issuer: DigiCert EV Code Signing CA (SHA2)&#10;Valid from 11/30/2016 to 12/05/2019"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.FirewallExceptionComponent">
+ <ROW FirewallException="ZeroTierOneControl" DisplayName="ZeroTier One TCP/9993" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="1" Profiles="7" Port="9993" Protocol="TCP"/>
+ <ROW FirewallException="ZeroTierOneUDP9993" DisplayName="ZeroTier One UDP/9993" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="1" Profiles="7" Port="9993" Protocol="UDP"/>
+ <ROW FirewallException="ZeroTierOnex64Binary" DisplayName="ZeroTier One x64 Binary" Enabled="1" Scope="*" Condition="((?zerotierone_x64.exe=2) AND ($zerotierone_x64.exe=3))" Profiles="0" AppPath="[#zerotierone_x64.exe]" Port="*" Protocol="ANY"/>
+ <ROW FirewallException="ZeroTierOnex86Binary" DisplayName="ZeroTier One x86 Binary" Enabled="1" Scope="*" Condition="((?zerotierone_x86.exe=2) AND ($zerotierone_x86.exe=3))" Profiles="0" AppPath="[#zerotierone_x86.exe]" Port="*" Protocol="ANY"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.FragmentComponent">
+ <ROW Fragment="CommonUI.aip" Path="&lt;AI_FRAGS&gt;CommonUI.aip"/>
+ <ROW Fragment="MaintenanceTypeDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\MaintenanceTypeDlg.aip"/>
+ <ROW Fragment="MaintenanceWelcomeDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\MaintenanceWelcomeDlg.aip"/>
+ <ROW Fragment="SequenceDialogs.aip" Path="&lt;AI_THEMES&gt;classic\fragments\SequenceDialogs.aip"/>
+ <ROW Fragment="Sequences.aip" Path="&lt;AI_FRAGS&gt;Sequences.aip"/>
+ <ROW Fragment="StaticUIStrings.aip" Path="&lt;AI_FRAGS&gt;StaticUIStrings.aip"/>
+ <ROW Fragment="UI.aip" Path="&lt;AI_THEMES&gt;classic\fragments\UI.aip"/>
+ <ROW Fragment="Validation.aip" Path="&lt;AI_FRAGS&gt;Validation.aip"/>
+ <ROW Fragment="VerifyRemoveDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\VerifyRemoveDlg.aip"/>
+ <ROW Fragment="VerifyRepairDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\VerifyRepairDlg.aip"/>
+ <ROW Fragment="WelcomeDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\WelcomeDlg.aip"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiActionTextComponent">
+ <ROW Action="AI_AiBackupImmediate" Description="Preparing backup operation" DescriptionLocId="ActionText.Description.AI_AiBackupImmediate" Template="Path: [1]" TemplateLocId="ActionText.Template.AI_AiBackupImmediate"/>
+ <ROW Action="AI_AiBackupRollback" Description="Rollback backup" DescriptionLocId="ActionText.Description.AI_AiBackupRollback" Template="Path: [1]" TemplateLocId="ActionText.Template.AI_AiBackupRollback"/>
+ <ROW Action="AI_AiRestoreDeferred" Description="Executing restore operation" DescriptionLocId="ActionText.Description.AI_AiRestoreDeferred" Template="Path: [1]" TemplateLocId="ActionText.Template.AI_AiRestoreDeferred"/>
+ <ROW Action="AI_AiRestoreDeferredImpersonate" Description="Executing restore operation" DescriptionLocId="ActionText.Description.AI_AiRestoreDeferred" Template="Path: [1]" TemplateLocId="ActionText.Template.AI_AiRestoreDeferred"/>
+ <ROW Action="AI_AiRestoreRollback" Description="Rollback restore" DescriptionLocId="ActionText.Description.AI_AiRestoreRollback" Template="Path: [1]" TemplateLocId="ActionText.Template.AI_AiRestoreRollback"/>
+ <ROW Action="AI_DeleteLzma" Description="Deleting files extracted from archive" DescriptionLocId="ActionText.Description.AI_DeleteLzma" TemplateLocId="-"/>
+ <ROW Action="AI_DeleteRLzma" Description="Deleting files extracted from archive" DescriptionLocId="ActionText.Description.AI_DeleteLzma" TemplateLocId="-"/>
+ <ROW Action="AI_ExtractFiles" Description="Extracting files from archive" DescriptionLocId="ActionText.Description.AI_ExtractLzma" TemplateLocId="-"/>
+ <ROW Action="AI_ExtractLzma" Description="Extracting files from archive" DescriptionLocId="ActionText.Description.AI_ExtractLzma" TemplateLocId="-"/>
+ <ROW Action="AI_FwConfig" Description="Executing Windows Firewall configurations" DescriptionLocId="ActionText.Description.AI_FwConfig" Template="Configuring Windows Firewall rule: &quot;[1]&quot;" TemplateLocId="ActionText.Template.AI_FwConfig"/>
+ <ROW Action="AI_FwInstall" Description="Generating actions to configure Windows Firewall" DescriptionLocId="ActionText.Description.AI_FwInstall"/>
+ <ROW Action="AI_FwRemove" Description="Executing Windows Firewall configurations" DescriptionLocId="ActionText.Description.AI_FwRemove" Template="Configuring Windows Firewall rule: &quot;[1]&quot;" TemplateLocId="ActionText.Template.AI_FwRemove"/>
+ <ROW Action="AI_FwRollback" Description="Rolling back Windows Firewall configurations." DescriptionLocId="ActionText.Description.AI_FwRollback" Template="Rolling back Windows Firewall configurations." TemplateLocId="ActionText.Template.AI_FwRollback"/>
+ <ROW Action="AI_FwUninstall" Description="Generating actions to configure Windows Firewall" DescriptionLocId="ActionText.Description.AI_FwUninstall"/>
+ <ROW Action="AI_TxtUpdaterCommit" Description="Commit text file changes. " DescriptionLocId="ActionText.Description.AI_TxtUpdaterCommit" Template="Commit text file changes." TemplateLocId="ActionText.Template.AI_TxtUpdaterCommit"/>
+ <ROW Action="AI_TxtUpdaterConfig" Description="Executing text file updates" DescriptionLocId="ActionText.Description.AI_TxtUpdaterConfig" Template="Updating text file: &quot;[1]&quot;" TemplateLocId="ActionText.Template.AI_TxtUpdaterConfig"/>
+ <ROW Action="AI_TxtUpdaterInstall" Description="Generating actions to configure text files updates" DescriptionLocId="ActionText.Description.AI_TxtUpdaterInstall"/>
+ <ROW Action="AI_TxtUpdaterRollback" Description="Rolling back text file changes. " DescriptionLocId="ActionText.Description.AI_TxtUpdaterRollback" Template="Rolling back text file changes." TemplateLocId="ActionText.Template.AI_TxtUpdaterRollback"/>
+ <ROW Action="AI_XmlCommit" Description="Committing XML file configurations." DescriptionLocId="ActionText.Description.AI_XmlCommit" Template="Committing XML file configurations." TemplateLocId="ActionText.Template.AI_XmlCommit"/>
+ <ROW Action="AI_XmlConfig" Description="Executing XML file configurations" DescriptionLocId="ActionText.Description.AI_XmlConfig" Template="Configuring XML file: &quot;[1]&quot;" TemplateLocId="ActionText.Template.AI_XmlConfig"/>
+ <ROW Action="AI_XmlInstall" Description="Generating actions to configure XML files" DescriptionLocId="ActionText.Description.AI_XmlInstall"/>
+ <ROW Action="AI_XmlRemove" Description="Executing XML file configurations" DescriptionLocId="ActionText.Description.AI_XmlRemove" Template="Configuring XML file: &quot;[1]&quot;" TemplateLocId="ActionText.Template.AI_XmlRemove"/>
+ <ROW Action="AI_XmlRollback" Description="Rolling back XML file configurations." DescriptionLocId="ActionText.Description.AI_XmlRollback" Template="Rolling back XML file configurations." TemplateLocId="ActionText.Template.AI_XmlRollback"/>
+ <ROW Action="AI_XmlUninstall" Description="Generating actions to configure XML files" DescriptionLocId="ActionText.Description.AI_XmlUninstall"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiAppSearchComponent">
+ <ROW Property="AI_SETUPEXEPATH" Signature_="AI_EXE_PATH_CU" Builds="ExeBuild"/>
+ <ROW Property="AI_SETUPEXEPATH" Signature_="AI_EXE_PATH_LM" Builds="ExeBuild"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiBinaryComponent">
+ <ROW Name="ExternalUICleaner.dll" SourcePath="&lt;AI_CUSTACTS&gt;ExternalUICleaner.dll"/>
+ <ROW Name="NetFirewall.dll" SourcePath="&lt;AI_CUSTACTS&gt;NetFirewall.dll"/>
+ <ROW Name="Prereq.dll" SourcePath="&lt;AI_CUSTACTS&gt;Prereq.dll"/>
+ <ROW Name="ResourceCleaner.dll" SourcePath="&lt;AI_CUSTACTS&gt;ResourceCleaner.dll"/>
+ <ROW Name="SoftwareDetector.dll" SourcePath="&lt;AI_CUSTACTS&gt;SoftwareDetector.dll"/>
+ <ROW Name="TxtUpdater.dll" SourcePath="&lt;AI_CUSTACTS&gt;TxtUpdater.dll"/>
+ <ROW Name="aicustact.dll" SourcePath="&lt;AI_CUSTACTS&gt;aicustact.dll"/>
+ <ROW Name="chainersupport.dll" SourcePath="&lt;AI_CUSTACTS&gt;chainersupport.dll"/>
+ <ROW Name="lzmaextractor.dll" SourcePath="&lt;AI_CUSTACTS&gt;lzmaextractor.dll"/>
+ <ROW Name="msichainer.exe" SourcePath="&lt;AI_CUSTACTS&gt;msichainer.exe"/>
+ <ROW Name="xmlCfg.dll" SourcePath="&lt;AI_CUSTACTS&gt;xmlCfg.dll"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiConditionComponent">
+ <ROW Feature_="zttap300" Level="0" Condition="((VersionNT &lt; 500) OR (NOT VersionNT))"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiControlComponent">
+ <ROW Dialog_="WelcomeDlg" Control="WelcomeDlgDialogInitializer" Type="DialogInitializer" X="0" Y="0" Width="0" Height="0" Attributes="0" Order="-1" TextLocId="-" HelpLocId="-" ExtDataLocId="-"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiControlEventComponent">
+ <ROW Dialog_="WelcomeDlg" Control_="Next" Event="EndDialog" Argument="Return" Condition="AI_INSTALL" Ordering="1"/>
+ <ROW Dialog_="MaintenanceWelcomeDlg" Control_="Next" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT" Ordering="99"/>
+ <ROW Dialog_="CustomizeDlg" Control_="Next" Event="NewDialog" Argument="VerifyReadyDlg" Condition="AI_MAINT" Ordering="101"/>
+ <ROW Dialog_="CustomizeDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT" Ordering="1"/>
+ <ROW Dialog_="VerifyReadyDlg" Control_="Install" Event="EndDialog" Argument="Return" Condition="AI_MAINT" Ordering="198"/>
+ <ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="CustomizeDlg" Condition="AI_MAINT" Ordering="202"/>
+ <ROW Dialog_="MaintenanceTypeDlg" Control_="ChangeButton" Event="NewDialog" Argument="CustomizeDlg" Condition="AI_MAINT" Ordering="501"/>
+ <ROW Dialog_="MaintenanceTypeDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceWelcomeDlg" Condition="AI_MAINT" Ordering="1"/>
+ <ROW Dialog_="MaintenanceTypeDlg" Control_="RemoveButton" Event="NewDialog" Argument="VerifyRemoveDlg" Condition="AI_MAINT AND InstallMode=&quot;Remove&quot;" Ordering="601"/>
+ <ROW Dialog_="VerifyRemoveDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT AND InstallMode=&quot;Remove&quot;" Ordering="1"/>
+ <ROW Dialog_="MaintenanceTypeDlg" Control_="RepairButton" Event="NewDialog" Argument="VerifyRepairDlg" Condition="AI_MAINT AND InstallMode=&quot;Repair&quot;" Ordering="601"/>
+ <ROW Dialog_="VerifyRepairDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT AND InstallMode=&quot;Repair&quot;" Ordering="1"/>
+ <ROW Dialog_="VerifyRepairDlg" Control_="Repair" Event="EndDialog" Argument="Return" Condition="AI_MAINT AND InstallMode=&quot;Repair&quot;" Ordering="399" Options="1"/>
+ <ROW Dialog_="VerifyRemoveDlg" Control_="Remove" Event="EndDialog" Argument="Return" Condition="AI_MAINT AND InstallMode=&quot;Remove&quot;" Ordering="299" Options="1"/>
+ <ROW Dialog_="PatchWelcomeDlg" Control_="Next" Event="NewDialog" Argument="VerifyReadyDlg" Condition="AI_PATCH" Ordering="201"/>
+ <ROW Dialog_="VerifyReadyDlg" Control_="Install" Event="EndDialog" Argument="Return" Condition="AI_PATCH" Ordering="199"/>
+ <ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="PatchWelcomeDlg" Condition="AI_PATCH" Ordering="203"/>
+ <ROW Dialog_="ResumeDlg" Control_="Install" Event="EndDialog" Argument="Return" Condition="AI_RESUME" Ordering="299"/>
+ <ROW Dialog_="WelcomeDlg" Control_="Next" Event="SpawnDialog" Argument="OutOfRbDiskDlg" Condition="AI_INSTALL AND OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND (PROMPTROLLBACKCOST=&quot;P&quot; OR NOT PROMPTROLLBACKCOST)" Ordering="2" Options="2"/>
+ <ROW Dialog_="WelcomeDlg" Control_="Next" Event="EnableRollback" Argument="False" Condition="AI_INSTALL AND OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST=&quot;D&quot;" Ordering="3" Options="2"/>
+ <ROW Dialog_="WelcomeDlg" Control_="Next" Event="SpawnDialog" Argument="OutOfDiskDlg" Condition="AI_INSTALL AND ( (OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 1) OR (OutOfDiskSpace = 1 AND PROMPTROLLBACKCOST=&quot;F&quot;) )" Ordering="4" Options="2"/>
+ <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[AI_ButtonText_Next_Orig]" Argument="[ButtonText_Next]" Condition="AI_INSTALL" Ordering="0" Options="2"/>
+ <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[ButtonText_Next]" Argument="[[AI_CommitButton]]" Condition="AI_INSTALL" Ordering="1" Options="2"/>
+ <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[AI_Text_Next_Orig]" Argument="[Text_Next]" Condition="AI_INSTALL" Ordering="2" Options="2"/>
+ <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[Text_Next]" Argument="[Text_Install]" Condition="AI_INSTALL" Ordering="3" Options="2"/>
+ <ROW Dialog_="WelcomeDlg" Control_="Back" Event="[ButtonText_Next]" Argument="[AI_ButtonText_Next_Orig]" Condition="AI_INSTALL" Ordering="0" Options="2"/>
+ <ROW Dialog_="WelcomeDlg" Control_="Back" Event="[Text_Next]" Argument="[AI_Text_Next_Orig]" Condition="AI_INSTALL" Ordering="1" Options="2"/>
+ <ROW Dialog_="FatalError" Control_="Finish" Event="DoAction" Argument="AI_AiBackupCleanup" Condition="1" Ordering="102"/>
+ <ROW Dialog_="UserExit" Control_="Finish" Event="DoAction" Argument="AI_AiBackupCleanup" Condition="1" Ordering="101"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiCreateFolderComponent">
+ <ROW Directory_="networks.d_Dir" Component_="networks.d" ManualDelete="false"/>
+ <ROW Directory_="regid.201001.com.zerotier_Dir" Component_="regid.201001.com.zerotier" ManualDelete="false"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiCustActComponent">
+ <ROW Action="AI_AiBackupCleanup" Type="1" Source="ResourceCleaner.dll" Target="OnAiBackupCleanup" WithoutSeq="true"/>
+ <ROW Action="AI_AiBackupImmediate" Type="1" Source="ResourceCleaner.dll" Target="OnAiBackupImmediate"/>
+ <ROW Action="AI_AiBackupRollback" Type="11521" Source="ResourceCleaner.dll" Target="OnAiBackupRollback"/>
+ <ROW Action="AI_AiRestoreDeferred" Type="11265" Source="ResourceCleaner.dll" Target="OnAiRestoreDeferred"/>
+ <ROW Action="AI_AiRestoreDeferredImpersonate" Type="9217" Source="ResourceCleaner.dll" Target="OnAiRestoreDeferredImpersonate"/>
+ <ROW Action="AI_AiRestoreRollback" Type="11521" Source="ResourceCleaner.dll" Target="OnAiRestoreRollback" WithoutSeq="true"/>
+ <ROW Action="AI_AppSearchEx" Type="1" Source="Prereq.dll" Target="DoAppSearchEx"/>
+ <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH_ORIGINAL" Target="[AI_SETUPEXEPATH]"/>
+ <ROW Action="AI_CommitChainers" Type="11841" Source="chainersupport.dll" Target="CommitChainedPackages" WithoutSeq="true"/>
+ <ROW Action="AI_DATA_SETTER" Type="51" Source="CustomActionData" Target="[~]"/>
+ <ROW Action="AI_DATA_SETTER_1" Type="51" Source="CustomActionData" Target="[~]"/>
+ <ROW Action="AI_DATA_SETTER_2" Type="51" Source="CustomActionData" Target="[~]"/>
+ <ROW Action="AI_DATA_SETTER_3" Type="51" Source="CustomActionData" Target="[~]"/>
+ <ROW Action="AI_DATA_SETTER_4" Type="51" Source="AI_ExtractFiles" Target="[AI_SETUPEXEPATH]"/>
+ <ROW Action="AI_DATA_SETTER_6" Type="51" Source="CustomActionData" Target="ZeroTier One.exe"/>
+ <ROW Action="AI_DOWNGRADE" Type="19" Target="4010"/>
+ <ROW Action="AI_DeleteCadLzma" Type="51" Source="AI_DeleteLzma" Target="[AI_SETUPEXEPATH]"/>
+ <ROW Action="AI_DeleteLzma" Type="1025" Source="lzmaextractor.dll" Target="DeleteLZMAFiles"/>
+ <ROW Action="AI_DeleteRCadLzma" Type="51" Source="AI_DeleteRLzma" Target="[AI_SETUPEXEPATH]"/>
+ <ROW Action="AI_DeleteRLzma" Type="1281" Source="lzmaextractor.dll" Target="DeleteLZMAFiles"/>
+ <ROW Action="AI_DetectSoftware" Type="257" Source="SoftwareDetector.dll" Target="OnDetectSoftware"/>
+ <ROW Action="AI_DoRemoveExternalUIStub" Type="3585" Source="ExternalUICleaner.dll" Target="DoRemoveExternalUIStub" WithoutSeq="true"/>
+ <ROW Action="AI_DpiContentScale" Type="1" Source="aicustact.dll" Target="DpiContentScale"/>
+ <ROW Action="AI_EnableDebugLog" Type="321" Source="aicustact.dll" Target="EnableDebugLog"/>
+ <ROW Action="AI_EstimateExtractFiles" Type="1" Source="Prereq.dll" Target="EstimateExtractFiles"/>
+ <ROW Action="AI_ExtractCadLzma" Type="51" Source="AI_ExtractLzma" Target="[AI_SETUPEXEPATH]"/>
+ <ROW Action="AI_ExtractFiles" Type="1025" Source="Prereq.dll" Target="ExtractSourceFiles" AdditionalSeq="AI_DATA_SETTER_4"/>
+ <ROW Action="AI_ExtractLzma" Type="1025" Source="lzmaextractor.dll" Target="ExtractLZMAFiles"/>
+ <ROW Action="AI_FindExeLzma" Type="1" Source="lzmaextractor.dll" Target="FindEXE"/>
+ <ROW Action="AI_FwConfig" Type="11265" Source="NetFirewall.dll" Target="OnFwConfig" WithoutSeq="true"/>
+ <ROW Action="AI_FwInstall" Type="1" Source="NetFirewall.dll" Target="OnFwInstall" AdditionalSeq="AI_DATA_SETTER_2"/>
+ <ROW Action="AI_FwRemove" Type="11265" Source="NetFirewall.dll" Target="OnFwRemove" WithoutSeq="true"/>
+ <ROW Action="AI_FwRollback" Type="11521" Source="NetFirewall.dll" Target="OnFwRollback" WithoutSeq="true"/>
+ <ROW Action="AI_FwUninstall" Type="1" Source="NetFirewall.dll" Target="OnFwUninstall" AdditionalSeq="AI_DATA_SETTER_3"/>
+ <ROW Action="AI_GetArpIconPath" Type="1" Source="aicustact.dll" Target="GetArpIconPath"/>
+ <ROW Action="AI_InstallModeCheck" Type="1" Source="aicustact.dll" Target="UpdateInstallMode" WithoutSeq="true"/>
+ <ROW Action="AI_LaunchApp" Type="1" Source="aicustact.dll" Target="[#ZeroTierOne.exe]"/>
+ <ROW Action="AI_PREPARE_UPGRADE" Type="65" Source="aicustact.dll" Target="PrepareUpgrade"/>
+ <ROW Action="AI_PrepareChainers" Type="1" Source="chainersupport.dll" Target="PrepareChainedPackages"/>
+ <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH" Target="[AI_SETUPEXEPATH_ORIGINAL]"/>
+ <ROW Action="AI_RESTORE_LOCATION" Type="65" Source="aicustact.dll" Target="RestoreLocation"/>
+ <ROW Action="AI_RemoveExternalUIStub" Type="1" Source="ExternalUICleaner.dll" Target="RemoveExternalUIStub"/>
+ <ROW Action="AI_ResolveKnownFolders" Type="1" Source="aicustact.dll" Target="AI_ResolveKnownFolders"/>
+ <ROW Action="AI_RollbackChainers" Type="11585" Source="chainersupport.dll" Target="RollbackChainedPackages" WithoutSeq="true"/>
+ <ROW Action="AI_SHOW_LOG" Type="65" Source="aicustact.dll" Target="LaunchLogFile" WithoutSeq="true"/>
+ <ROW Action="AI_STORE_LOCATION" Type="51" Source="ARPINSTALLLOCATION" Target="[APPDIR]"/>
+ <ROW Action="AI_TxtUpdaterCommit" Type="11777" Source="TxtUpdater.dll" Target="OnTxtUpdaterCommit" WithoutSeq="true"/>
+ <ROW Action="AI_TxtUpdaterConfig" Type="11265" Source="TxtUpdater.dll" Target="OnTxtUpdaterConfig" WithoutSeq="true"/>
+ <ROW Action="AI_TxtUpdaterInstall" Type="1" Source="TxtUpdater.dll" Target="OnTxtUpdaterInstall"/>
+ <ROW Action="AI_TxtUpdaterRollback" Type="11521" Source="TxtUpdater.dll" Target="OnTxtUpdaterRollback" WithoutSeq="true"/>
+ <ROW Action="AI_XmlCommit" Type="11777" Source="xmlCfg.dll" Target="OnXmlCommit" WithoutSeq="true"/>
+ <ROW Action="AI_XmlConfig" Type="11265" Source="xmlCfg.dll" Target="OnXmlConfig" WithoutSeq="true"/>
+ <ROW Action="AI_XmlInstall" Type="1" Source="xmlCfg.dll" Target="OnXmlInstall" AdditionalSeq="AI_DATA_SETTER"/>
+ <ROW Action="AI_XmlRemove" Type="11265" Source="xmlCfg.dll" Target="OnXmlRemove" WithoutSeq="true"/>
+ <ROW Action="AI_XmlRollback" Type="11521" Source="xmlCfg.dll" Target="OnXmlRollback" WithoutSeq="true"/>
+ <ROW Action="AI_XmlUninstall" Type="1" Source="xmlCfg.dll" Target="OnXmlUninstall" AdditionalSeq="AI_DATA_SETTER_1"/>
+ <ROW Action="SET_APPDIR" Type="307" Source="APPDIR" Target="[ProgramFilesFolder][Manufacturer]\[ProductName]" MultiBuildTarget="DefaultBuild:[ProgramFilesFolder]ZeroTier\One"/>
+ <ROW Action="SET_SHORTCUTDIR" Type="307" Source="SHORTCUTDIR" Target="[ProgramMenuFolder][ProductName]" MultiBuildTarget="DefaultBuild:[ProgramMenuFolder]"/>
+ <ROW Action="SET_TARGETDIR_TO_APPDIR" Type="51" Source="TARGETDIR" Target="[APPDIR]"/>
+ <ROW Action="TapDeviceRemove32" Type="3154" Source="zerotierone_x86.exe" Target="-D"/>
+ <ROW Action="TapDeviceRemove64" Type="3154" Source="zerotierone_x64.exe" Target="-D"/>
+ <ROW Action="TerminateUI" Type="65" Source="aicustact.dll" Target="StopProcess" Options="1" AdditionalSeq="AI_DATA_SETTER_6"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiEmbeddedChainerComponent">
+ <ROW MsiEmbeddedChainer="msichainer.exe" Condition="VersionMsi &gt;= &quot;4.05&quot;" CommandLine="[AI_CHAINER_CMD_LINE]" Source="msichainer.exe" Type="2"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiEnvComponent">
+ <ROW Environment="Path" Name="=-*Path" Value="[~];[APPDIR]" Component_="ZeroTierOne.exe"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiFontsComponent">
+ <ROW File_="segoeui.ttf"/>
+ <ROW File_="segoeuib.ttf"/>
+ <ROW File_="segoeuii.ttf"/>
+ <ROW File_="segoeuiz.ttf"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiIconsComponent">
+ <ROW Name="ZeroTierIcon.exe" SourcePath="..\..\..\artwork\ZeroTierIcon.ico" Index="0"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiInstExSeqComponent">
+ <ROW Action="AI_DOWNGRADE" Condition="AI_NEWERPRODUCTFOUND AND (UILevel &lt;&gt; 5)" Sequence="210"/>
+ <ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
+ <ROW Action="AI_STORE_LOCATION" Condition="(Not Installed) OR REINSTALL" Sequence="1503"/>
+ <ROW Action="AI_PREPARE_UPGRADE" Condition="AI_UPGRADE=&quot;No&quot; AND (Not Installed)" Sequence="1399"/>
+ <ROW Action="AI_ResolveKnownFolders" Sequence="52"/>
+ <ROW Action="AI_XmlInstall" Condition="(REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5103"/>
+ <ROW Action="AI_DATA_SETTER" Condition="(REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5102"/>
+ <ROW Action="AI_XmlUninstall" Condition="(REMOVE)" Sequence="3102"/>
+ <ROW Action="AI_DATA_SETTER_1" Condition="(REMOVE)" Sequence="3101"/>
+ <ROW Action="InstallFinalize" Sequence="6600" SeqType="0" MsiKey="InstallFinalize"/>
+ <ROW Action="AI_RemoveExternalUIStub" Condition="(REMOVE=&quot;ALL&quot;) AND ((VersionNT &gt; 500) OR((VersionNT = 500) AND (ServicePackLevel &gt;= 4)))" Sequence="1502"/>
+ <ROW Action="AI_GetArpIconPath" Sequence="1402"/>
+ <ROW Action="TapDeviceRemove32" Condition="( Installed AND ( REMOVE = &quot;ALL&quot; OR AI_INSTALL_MODE = &quot;Remove&quot; ) AND NOT UPGRADINGPRODUCTCODE ) AND ( NOT VersionNT64 )" Sequence="1603"/>
+ <ROW Action="TapDeviceRemove64" Condition="( Installed AND ( REMOVE = &quot;ALL&quot; OR AI_INSTALL_MODE = &quot;Remove&quot; ) AND NOT UPGRADINGPRODUCTCODE ) AND ( VersionNT64 )" Sequence="1604"/>
+ <ROW Action="AI_FwInstall" Condition="(VersionNT &gt;= 501) AND (REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5802"/>
+ <ROW Action="AI_DATA_SETTER_2" Condition="(VersionNT &gt;= 501) AND (REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5801"/>
+ <ROW Action="AI_FwUninstall" Condition="(VersionNT &gt;= 501) AND (REMOVE=&quot;ALL&quot;)" Sequence="1702"/>
+ <ROW Action="AI_DATA_SETTER_3" Condition="(VersionNT &gt;= 501) AND (REMOVE=&quot;ALL&quot;)" Sequence="1701"/>
+ <ROW Action="AI_DetectSoftware" Sequence="103"/>
+ <ROW Action="AI_TxtUpdaterInstall" Sequence="5101"/>
+ <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Sequence="99" Builds="ExeBuild"/>
+ <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Condition="AI_SETUPEXEPATH_ORIGINAL" Sequence="102" Builds="ExeBuild"/>
+ <ROW Action="AI_DeleteCadLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="199" Builds="ExeBuild"/>
+ <ROW Action="AI_DeleteRCadLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="198" Builds="ExeBuild"/>
+ <ROW Action="AI_ExtractCadLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="197" Builds="ExeBuild"/>
+ <ROW Action="AI_FindExeLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="196" Builds="ExeBuild"/>
+ <ROW Action="AI_ExtractLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="1549" Builds="ExeBuild"/>
+ <ROW Action="AI_DeleteRLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="1548" Builds="ExeBuild"/>
+ <ROW Action="AI_DeleteLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="6594" Builds="ExeBuild"/>
+ <ROW Action="AI_ExtractFiles" Sequence="3998" Builds="ExeBuild"/>
+ <ROW Action="AI_DATA_SETTER_4" Sequence="3997"/>
+ <ROW Action="AI_EstimateExtractFiles" Sequence="3999" Builds="ExeBuild"/>
+ <ROW Action="TerminateUI" Sequence="1602"/>
+ <ROW Action="AI_DATA_SETTER_6" Sequence="1601"/>
+ <ROW Action="AI_AiBackupImmediate" Sequence="1401"/>
+ <ROW Action="AI_AiBackupRollback" Sequence="1501"/>
+ <ROW Action="AI_AiRestoreDeferred" Sequence="6595"/>
+ <ROW Action="AI_EnableDebugLog" Sequence="51"/>
+ <ROW Action="AI_AiRestoreDeferredImpersonate" Sequence="6596"/>
+ <ROW Action="AI_AppSearchEx" Sequence="101"/>
+ <ROW Action="AI_PrepareChainers" Condition="VersionMsi &gt;= &quot;4.05&quot;" Sequence="5851"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiInstallUISequenceComponent">
+ <ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
+ <ROW Action="AI_ResolveKnownFolders" Sequence="53"/>
+ <ROW Action="AI_DpiContentScale" Sequence="52"/>
+ <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Sequence="99"/>
+ <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Condition="AI_SETUPEXEPATH_ORIGINAL" Sequence="103"/>
+ <ROW Action="ExecuteAction" Sequence="1299" SeqType="0" MsiKey="ExecuteAction"/>
+ <ROW Action="AI_DetectSoftware" Sequence="102"/>
+ <ROW Action="AI_EnableDebugLog" Sequence="51"/>
+ <ROW Action="AI_AppSearchEx" Sequence="101"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiLaunchConditionsComponent">
+ <ROW Condition="( Version9X OR ( NOT VersionNT64 ) OR ( VersionNT64 AND ((VersionNT64 &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2) OR (MsiNTProductType &lt;&gt; 1)) AND ((VersionNT64 &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2) OR (MsiNTProductType = 1)) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT64Display]." DescriptionLocId="AI.LaunchCondition.NoSpecificNT64" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="( Version9X OR VersionNT64 OR ( VersionNT AND ((VersionNT &lt;&gt; 501) OR (ServicePackLevel &lt;&gt; 3)) AND ((VersionNT &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2)) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNTDisplay]." DescriptionLocId="AI.LaunchCondition.NoSpecificNT" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="(VersionNT &lt;&gt; 400)" Description="[ProductName] cannot be installed on [WindowsTypeNT40Display]." DescriptionLocId="AI.LaunchCondition.NoNT40" IsPredefined="true" Builds="DefaultBuild;ExeBuild"/>
+ <ROW Condition="(VersionNT &lt;&gt; 500)" Description="[ProductName] cannot be installed on [WindowsTypeNT50Display]." DescriptionLocId="AI.LaunchCondition.NoNT50" IsPredefined="true" Builds="DefaultBuild;ExeBuild"/>
+ <ROW Condition="(VersionNT64 OR ((VersionNT &lt;&gt; 501) OR (ServicePackLevel = 3))) AND ((VersionNT &lt;&gt; 502) OR (ServicePackLevel = 2))" Description="[ProductName] cannot be installed on [WindowsTypeNT5XDisplay]." DescriptionLocId="AI.LaunchCondition.NoNT5X" IsPredefined="true" Builds="DefaultBuild;ExeBuild"/>
+ <ROW Condition="AI_DETECTED_DOTNET_VERSION &gt;= AI_REQUIRED_DOTNET_VERSION" Description="[ProductName] cannot be installed on systems with .NET Framework version lower than [AI_REQUIRED_DOTNET_DISPLAY]." DescriptionLocId="AI.LaunchCondition.DotNET" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="Privileged" Description="[ProductName] requires administrative privileges to install." DescriptionLocId="AI.LaunchCondition.Privileged" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="SETUPEXEDIR OR (REMOVE=&quot;ALL&quot;)" Description="This package can only be run from a bootstrapper." DescriptionLocId="AI.LaunchCondition.RequireBootstrapper" IsPredefined="true" Builds="ExeBuild"/>
+ <ROW Condition="VersionNT" Description="[ProductName] cannot be installed on [WindowsType9XDisplay]." DescriptionLocId="AI.LaunchCondition.No9X" IsPredefined="true" Builds="DefaultBuild;ExeBuild"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiRegLocatorComponent">
+ <ROW Signature_="AI_EXE_PATH_CU" Root="1" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Type="2"/>
+ <ROW Signature_="AI_EXE_PATH_LM" Root="2" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Type="2"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiRegsComponent">
+ <ROW Registry="AI_ExePath" Root="-1" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Value="[AI_SETUPEXEPATH]" Component_="AI_ExePath"/>
+ <ROW Registry="Comments" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="Comments" Value="[ARPCOMMENTS]" Component_="AI_CustomARPName"/>
+ <ROW Registry="Contact" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="Contact" Value="[ARPCONTACT]" Component_="AI_CustomARPName"/>
+ <ROW Registry="DisplayIcon" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="DisplayIcon" Value="[ARP_ICON_PATH]" Component_="AI_CustomARPName"/>
+ <ROW Registry="DisplayName" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="DisplayName" Value="[AI_PRODUCTNAME_ARP]" Component_="AI_CustomARPName"/>
+ <ROW Registry="DisplayVersion" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="DisplayVersion" Value="[ProductVersion]" Component_="AI_CustomARPName"/>
+ <ROW Registry="HelpLink" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="HelpLink" Value="[ARPHELPLINK]" Component_="AI_CustomARPName"/>
+ <ROW Registry="HelpTelephone" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="HelpTelephone" Value="[ARPHELPTELEPHONE]" Component_="AI_CustomARPName"/>
+ <ROW Registry="InstallLocation" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="InstallLocation" Value="[APPDIR]" Component_="AI_CustomARPName"/>
+ <ROW Registry="ModifyPath" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="ModifyPath" Value="[AI_UNINSTALLER] /I [ProductCode]" Component_="AI_CustomARPName"/>
+ <ROW Registry="NoModify" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="NoModify" Value="#1" Component_="AI_DisableModify"/>
+ <ROW Registry="NoRepair" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="NoRepair" Value="#1" Component_="AI_CustomARPName"/>
+ <ROW Registry="Path" Root="-1" Key="Software\[Manufacturer]\[ProductName]" Name="Path" Value="[APPDIR]" Component_="ProductInformation"/>
+ <ROW Registry="Publisher" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="Publisher" Value="[Manufacturer]" Component_="AI_CustomARPName"/>
+ <ROW Registry="URLInfoAbout" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="URLInfoAbout" Value="[ARPURLINFOABOUT]" Component_="AI_CustomARPName"/>
+ <ROW Registry="URLUpdateInfo" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="URLUpdateInfo" Value="[ARPURLUPDATEINFO]" Component_="AI_CustomARPName"/>
+ <ROW Registry="UninstallPath" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="UninstallPath" Value="[AI_UNINSTALLER] /x [ProductCode] AI_UNINSTALLER_CTP=1" Component_="AI_CustomARPName"/>
+ <ROW Registry="UninstallString" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="UninstallString" Value="[AI_UNINSTALLER] /x [ProductCode] AI_UNINSTALLER_CTP=1" Component_="AI_CustomARPName"/>
+ <ROW Registry="Version" Root="-1" Key="Software\[Manufacturer]\[ProductName]" Name="Version" Value="[ProductVersion]" Component_="ProductInformation"/>
+ <ROW Registry="VersionMajor" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="VersionMajor" Value="#0" Component_="AI_CustomARPName"/>
+ <ROW Registry="VersionMinor" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="VersionMinor" Value="#7" Component_="AI_CustomARPName"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiServCtrlComponent">
+ <ROW ServiceControl="zerotierone_x64.exe" Name="ZeroTierOneService" Event="163" Wait="1" Component_="zerotierone_x64.exe"/>
+ <ROW ServiceControl="zerotierone_x86.exe" Name="ZeroTierOneService" Event="163" Wait="1" Component_="zerotierone_x86.exe"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiServInstComponent">
+ <ROW ServiceInstall="zerotierone_x64.exe" Name="ZeroTierOneService" DisplayName="ZeroTier One" ServiceType="16" StartType="2" ErrorControl="32769" Component_="zerotierone_x64.exe" Description="Ethernet Virtualization Service"/>
+ <ROW ServiceInstall="zerotierone_x86.exe" Name="ZeroTierOneService" DisplayName="ZeroTier One" ServiceType="16" StartType="2" ErrorControl="32769" Component_="zerotierone_x86.exe" Description="Ethernet Virtualization Service"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiShortsComponent">
+ <ROW Shortcut="ZeroTierOne" Directory_="ProgramMenuFolder" Name="ZEROTI~1|ZeroTier One" Component_="ZeroTierOne.exe" Target="[#ZeroTierOne.exe]" Description="Ethernet Virtualization Control Panel" Hotkey="0" Icon_="ZeroTierIcon.exe" IconIndex="0" ShowCmd="1" WkDir="APPDIR"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiThemeComponent">
+ <ATTRIBUTE name="UsedTheme" value="classic"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiUpgradeComponent">
+ <ROW UpgradeCode="[|UpgradeCode]" VersionMin="0.0.1" VersionMax="[|ProductVersion]" Attributes="257" ActionProperty="OLDPRODUCTS"/>
+ <ROW UpgradeCode="[|UpgradeCode]" VersionMin="[|ProductVersion]" Attributes="2" ActionProperty="AI_NEWERPRODUCTFOUND"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.PreReqSearchComponent">
+ <ROW SearchKey="UpgradeCode" SearchType="4" SearchString="{88AA80DE-14CA-4443-B024-6EC13F3EDDAD}" Order="2" Property="ZTTAP300_X86_INSTALLED"/>
+ <ROW SearchKey="_" SearchType="4" SearchString="{88AA80DE-14CA-4443-B024-6EC13F3EDDAD}" Order="1" Property="ZTTAP300_X64_INSTALLED"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.SoftwareIdentificationComponent">
+ <ATTRIBUTE name="LocalFile" value="regid.199509.com.example_ProductName.swidtag"/>
+ <ATTRIBUTE name="SystemFile" value="regid.199509.com.example_ProductName.swidtag_1"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.TxtUpdateComponent">
+ <ROW Name="Append/Create" TxtUpdateSet="zerotiercli.bat" FindPattern="@ECHO OFF&#13;&#10;if [\[][#zerotierone_x64.exe][\]] == [\[][\]] (&#13;&#10;&#9;[#zerotierone_x86.exe] -q %*&#13;&#10;) else (&#13;&#10;&#9;[#zerotierone_x64.exe] -q %*&#13;&#10;)&#13;&#10;" Options="160" Order="0" FileEncoding="0"/>
+ <ROW Name="Replace" TxtUpdateSet="zerotiercli.bat" FindPattern="YourFindText" ReplacePattern="YourReplaceText" Options="2" Order="1" FileEncoding="-1"/>
+ <ROW Name="Append/Create" TxtUpdateSet="zerotiercli1.bat" FindPattern="@ECHO OFF&#13;&#10;if [\[][#zerotierone_x64.exe][\]] == [\[][\]] (&#13;&#10;&#9;[#zerotierone_x86.exe] -i %*&#13;&#10;) else (&#13;&#10;&#9;[#zerotierone_x64.exe] -i %*&#13;&#10;)&#13;&#10;" Options="160" Order="0" FileEncoding="0"/>
+ <ROW Name="Replace" TxtUpdateSet="zerotiercli1.bat" FindPattern="YourFindText" ReplacePattern="YourReplaceText" Options="2" Order="1" FileEncoding="-1"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.TxtUpdateSetComponent">
+ <ROW Key="zerotiercli.bat" Component="regid.201001.com.zerotier" FileName="zerotier-cli.bat" Directory="APPDIR" Options="17"/>
+ <ROW Key="zerotiercli1.bat" Component="regid.201001.com.zerotier" FileName="zerotier-idtool.bat" Directory="APPDIR" Options="17"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.XmlAttributeComponent">
+ <ROW XmlAttribute="xmlnsds" XmlElement="swidsoftware_identification_tag" Name="xmlns:ds" Flags="14" Order="0" Value="http://www.w3.org/2000/09/xmldsig#"/>
+ <ROW XmlAttribute="xmlnsswid" XmlElement="swidsoftware_identification_tag" Name="xmlns:swid" Flags="14" Order="1" Value="http://standards.iso.org/iso/19770/-2/2008/schema.xsd"/>
+ <ROW XmlAttribute="xmlnsxsi" XmlElement="swidsoftware_identification_tag" Name="xmlns:xsi" Flags="14" Order="2" Value="http://www.w3.org/2001/XMLSchema-instance"/>
+ <ROW XmlAttribute="xsischemaLocation" XmlElement="swidsoftware_identification_tag" Name="xsi:schemaLocation" Flags="14" Order="3" Value="http://standards.iso.org/iso/19770/-2/2008/schema.xsd software_identification_tag.xsd"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.XmlElementComponent">
+ <ROW XmlElement="swidbuild" ParentElement="swidnumeric" Name="swid:build" Condition="1" Order="2" Flags="14" Text="6"/>
+ <ROW XmlElement="swidentitlement_required_indicator" ParentElement="swidsoftware_identification_tag" Name="swid:entitlement_required_indicator" Condition="1" Order="0" Flags="14" Text="false"/>
+ <ROW XmlElement="swidmajor" ParentElement="swidnumeric" Name="swid:major" Condition="1" Order="0" Flags="14" Text="1"/>
+ <ROW XmlElement="swidminor" ParentElement="swidnumeric" Name="swid:minor" Condition="1" Order="1" Flags="14" Text="2"/>
+ <ROW XmlElement="swidname" ParentElement="swidproduct_version" Name="swid:name" Condition="1" Order="0" Flags="14" Text="[ProductVersion]"/>
+ <ROW XmlElement="swidname_1" ParentElement="swidsoftware_creator" Name="swid:name" Condition="1" Order="0" Flags="14" Text="ZeroTier, Inc."/>
+ <ROW XmlElement="swidname_2" ParentElement="swidsoftware_licensor" Name="swid:name" Condition="1" Order="0" Flags="14" Text="ZeroTier, Inc."/>
+ <ROW XmlElement="swidname_3" ParentElement="swidtag_creator" Name="swid:name" Condition="1" Order="0" Flags="14" Text="ZeroTier, Inc."/>
+ <ROW XmlElement="swidnumeric" ParentElement="swidproduct_version" Name="swid:numeric" Condition="1" Order="1" Flags="14"/>
+ <ROW XmlElement="swidproduct_title" ParentElement="swidsoftware_identification_tag" Name="swid:product_title" Condition="1" Order="1" Flags="14" Text="[ProductName]"/>
+ <ROW XmlElement="swidproduct_version" ParentElement="swidsoftware_identification_tag" Name="swid:product_version" Condition="1" Order="2" Flags="14"/>
+ <ROW XmlElement="swidregid" ParentElement="swidsoftware_creator" Name="swid:regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
+ <ROW XmlElement="swidregid_1" ParentElement="swidsoftware_licensor" Name="swid:regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
+ <ROW XmlElement="swidregid_2" ParentElement="swidtag_creator" Name="swid:regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
+ <ROW XmlElement="swidreview" ParentElement="swidnumeric" Name="swid:review" Condition="1" Order="3" Flags="14" Text="0"/>
+ <ROW XmlElement="swidsoftware_creator" ParentElement="swidsoftware_identification_tag" Name="swid:software_creator" Condition="1" Order="3" Flags="14"/>
+ <ROW XmlElement="swidsoftware_id" ParentElement="swidsoftware_identification_tag" Name="swid:software_id" Condition="1" Order="5" Flags="14"/>
+ <ROW XmlElement="swidsoftware_identification_tag" Name="swid:software_identification_tag" Condition="1" Order="0" Flags="14"/>
+ <ROW XmlElement="swidsoftware_licensor" ParentElement="swidsoftware_identification_tag" Name="swid:software_licensor" Condition="1" Order="4" Flags="14"/>
+ <ROW XmlElement="swidtag_creator" ParentElement="swidsoftware_identification_tag" Name="swid:tag_creator" Condition="1" Order="6" Flags="14"/>
+ <ROW XmlElement="swidtag_creator_regid" ParentElement="swidsoftware_id" Name="swid:tag_creator_regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
+ <ROW XmlElement="swidunique_id" ParentElement="swidsoftware_id" Name="swid:unique_id" Condition="1" Order="0" Flags="14" Text="ZeroTierOne"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.XmlFileComponent">
+ <ROW XmlFile="regid.199509.com.example_ProductName.swidtag" FileName="REGID2~1.SWI|regid.2010-01.com.zerotier_ZeroTierOne.swidtag" DirProperty="APPDIR" Component="ProductInformation" RootElement="swidsoftware_identification_tag" Flags="25" Version="1.0" Encoding="UTF-8" IndentUnits="2"/>
+ <ROW XmlFile="regid.199509.com.example_ProductName.swidtag_1" FileName="REGID2~1.SWI|regid.2010-01.com.zerotier_ZeroTierOne.swidtag" DirProperty="regid.201001.com.zerotier_Dir" Component="ProductInformation" RootElement="swidsoftware_identification_tag" Flags="25" Version="1.0" Encoding="UTF-8" IndentUnits="2"/>
+ </COMPONENT>
+</DOCUMENT>
diff --git a/ext/installfiles/windows/chocolatey/zerotier-one/tools/chocolateyinstall.ps1 b/ext/installfiles/windows/chocolatey/zerotier-one/tools/chocolateyinstall.ps1
index f8a7457e..9a0f6bac 100644
--- a/ext/installfiles/windows/chocolatey/zerotier-one/tools/chocolateyinstall.ps1
+++ b/ext/installfiles/windows/chocolatey/zerotier-one/tools/chocolateyinstall.ps1
@@ -1,7 +1,7 @@
$packageName = 'zerotier-one'
$installerType = 'msi'
-$url = 'https://download.zerotier.com/RELEASES/1.1.14/dist/ZeroTier%20One.msi'
-$url64 = 'https://download.zerotier.com/RELEASES/1.1.14/dist/ZeroTier%20One.msi'
+$url = 'https://download.zerotier.com/RELEASES/1.2.4/dist/ZeroTier%20One.msi'
+$url64 = 'https://download.zerotier.com/RELEASES/1.2.4/dist/ZeroTier%20One.msi'
$silentArgs = '/quiet'
$validExitCodes = @(0,3010)
diff --git a/ext/installfiles/windows/chocolatey/zerotier-one/zerotier-one.nuspec b/ext/installfiles/windows/chocolatey/zerotier-one/zerotier-one.nuspec
index 32fa5a9c..3d090a7b 100644
--- a/ext/installfiles/windows/chocolatey/zerotier-one/zerotier-one.nuspec
+++ b/ext/installfiles/windows/chocolatey/zerotier-one/zerotier-one.nuspec
@@ -8,8 +8,8 @@
This is a nuspec. It mostly adheres to https://docs.nuget.org/create/Nuspec-Reference. Chocolatey uses a special version of NuGet.Core that allows us to do more than was initially possible. As such there are certain things to be aware of:
* the package xmlns schema url may cause issues with nuget.exe
-* Any of the following elements can ONLY be used by choco tools - projectSourceUrl, docsUrl, mailingListUrl, bugTrackerUrl, packageSourceUrl, provides, conflicts, replaces
-* nuget.exe can still install packages with those elements but they are ignored. Any authoring tools or commands will error on those elements
+* Any of the following elements can ONLY be used by choco tools - projectSourceUrl, docsUrl, mailingListUrl, bugTrackerUrl, packageSourceUrl, provides, conflicts, replaces
+* nuget.exe can still install packages with those elements but they are ignored. Any authoring tools or commands will error on those elements
-->
<!-- You can embed software files directly into packages, as long as you are not bound by distribution rights. -->
@@ -26,7 +26,7 @@ This is a nuspec. It mostly adheres to https://docs.nuget.org/create/Nuspec-Refe
<!-- version should MATCH as closely as possible with the underlying software -->
<!-- Is the version a prerelease of a version? https://docs.nuget.org/create/versioning#creating-prerelease-packages -->
<!-- Note that unstable versions like 0.0.1 can be considered a released version, but it's possible that one can release a 0.0.1-beta before you release a 0.0.1 version. If the version number is final, that is considered a released version and not a prerelease. -->
- <version>1.2.4</version>
+ <version>1.2.6</version>
<!-- <packageSourceUrl>Where is this Chocolatey package located (think GitHub)? packageSourceUrl is highly recommended for the community feed</packageSourceUrl>-->
<!-- owners is a poor name for maintainers of the package. It sticks around by this name for compatibility reasons. It basically means you. -->
<!--<owners>ZeroTier, Inc.</owners>-->
@@ -51,7 +51,7 @@ This is a nuspec. It mostly adheres to https://docs.nuget.org/create/Nuspec-Refe
<summary>ZeroTier One Virtual Network Endpoint for Windows</summary>
<description>ZeroTier is a smart switch for Earth with VLAN capability. See https://www.zerotier.com/ for more information.</description>
<!-- <releaseNotes>__REPLACE_OR_REMOVE__MarkDown_Okay</releaseNotes> -->
- <!-- =============================== -->
+ <!-- =============================== -->
<!-- Specifying dependencies and version ranges? https://docs.nuget.org/create/versioning#specifying-version-ranges-in-.nuspec-files -->
<!--<dependencies>
diff --git a/ext/json/LICENSE.MIT b/ext/json/LICENSE.MIT
index e2ac4891..00599afe 100644
--- a/ext/json/LICENSE.MIT
+++ b/ext/json/LICENSE.MIT
@@ -1,14 +1,13 @@
-The library is licensed under the MIT License
-<http://opensource.org/licenses/MIT>:
+MIT License
-Copyright (c) 2013-2016 Niels Lohmann
+Copyright (c) 2013-2017 Niels Lohmann
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
diff --git a/ext/json/README.md b/ext/json/README.md
index 4bcbe97f..256409e3 100644
--- a/ext/json/README.md
+++ b/ext/json/README.md
@@ -3,13 +3,36 @@
[![Build Status](https://travis-ci.org/nlohmann/json.svg?branch=master)](https://travis-ci.org/nlohmann/json)
[![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk/branch/develop?svg=true)](https://ci.appveyor.com/project/nlohmann/json)
[![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json)
-[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/fsf5FqYe6GoX68W6)
+[![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json)
+[![Codacy Badge](https://api.codacy.com/project/badge/Grade/f3732b3327e34358a0e9d1fe9f661f08)](https://www.codacy.com/app/nlohmann/json?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=nlohmann/json&amp;utm_campaign=Badge_Grade)
+[![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/Op57X0V7fTf2tdwl)
[![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json)
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT)
[![Github Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases)
[![Github Issues](https://img.shields.io/github/issues/nlohmann/json.svg)](http://github.com/nlohmann/json/issues)
+[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/nlohmann/json.svg)](http://isitmaintained.com/project/nlohmann/json "Average time to resolve an issue")
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/289/badge)](https://bestpractices.coreinfrastructure.org/projects/289)
+- [Design goals](#design-goals)
+- [Integration](#integration)
+- [Examples](#examples)
+ - [JSON as first-class data type](#json-as-first-class-data-type)
+ - [Serialization / Deserialization](#serialization--deserialization)
+ - [STL-like access](#stl-like-access)
+ - [Conversion from STL containers](#conversion-from-stl-containers)
+ - [JSON Pointer and JSON Patch](#json-pointer-and-json-patch)
+ - [Implicit conversions](#implicit-conversions)
+ - [Conversions to/from arbitrary types](#arbitrary-types-conversions)
+ - [Binary formats (CBOR and MessagePack)](#binary-formats-cbor-and-messagepack)
+- [Supported compilers](#supported-compilers)
+- [License](#license)
+- [Contact](#contact)
+- [Thanks](#thanks)
+- [Used third-party tools](#used-third-party-tools)
+- [Projects using JSON for Modern C++](#projects-using-json-for-modern-c)
+- [Notes](#notes)
+- [Execute unit tests](#execute-unit-tests)
+
## Design goals
There are myriads of [JSON](http://json.org) libraries out there, and each may even have its reason to exist. Our class had these design goals:
@@ -24,7 +47,7 @@ Other aspects were not so important to us:
- **Memory efficiency**. Each JSON object has an overhead of one pointer (the maximal size of a union) and one enumeration element (1 byte). The default generalization uses the following C++ data types: `std::string` for strings, `int64_t`, `uint64_t` or `double` for numbers, `std::map` for objects, `std::vector` for arrays, and `bool` for Booleans. However, you can template the generalized class `basic_json` to your needs.
-- **Speed**. We currently implement the parser as naive [recursive descent parser](http://en.wikipedia.org/wiki/Recursive_descent_parser) with hand coded string handling. It is fast enough, but a [LALR-parser](http://en.wikipedia.org/wiki/LALR_parser) may be even faster (but would consist of more files which makes the integration harder).
+- **Speed**. There are certainly [faster JSON libraries](https://github.com/miloyip/nativejson-benchmark#parsing-time) out there. However, if your goal is to speed up your development by adding JSON support with a single header, then this library is the way to go. If you know how to use a `std::vector` or `std::map`, you are already set.
See the [contribution guidelines](https://github.com/nlohmann/json/blob/master/.github/CONTRIBUTING.md#please-dont) for more information.
@@ -44,9 +67,21 @@ to the files you want to use JSON objects. That's it. Do not forget to set the n
:beer: If you are using OS X and [Homebrew](http://brew.sh), just type `brew tap nlohmann/json` and `brew install nlohmann_json` and you're set. If you want the bleeding edge rather than the latest release, use `brew install nlohmann_json --HEAD`.
+If you are using the [Meson Build System](http://mesonbuild.com), then you can wrap this repo as a subproject.
+
+If you are using [Conan](https://www.conan.io/) to manage your dependencies, merely add `jsonformoderncpp/x.y.z@vthiery/stable` to your `conanfile.py`'s requires, where `x.y.z` is the release version you want to use. Please file issues [here](https://github.com/vthiery/conan-jsonformoderncpp/issues) if you experience problems with the packages.
+
+If you are using [hunter](https://github.com/ruslo/hunter/) on your project for external dependencies, then you can use the [nlohmann_json package](https://docs.hunter.sh/en/latest/packages/pkg/nlohmann_json.html). Please see the hunter project for any issues regarding the packaging.
+
+If you are using [vcpkg](https://github.com/Microsoft/vcpkg/) on your project for external dependencies, then you can use the [nlohmann-json package](https://github.com/Microsoft/vcpkg/tree/master/ports/nlohmann-json). Please see the vcpkg project for any issues regarding the packaging.
+
## Examples
+Beside the examples below, you may want to check the [documentation](https://nlohmann.github.io/json/) where each function contains a separate code example (e.g., check out [`emplace()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a602f275f0359ab181221384989810604.html#a602f275f0359ab181221384989810604)). All [example files](https://github.com/nlohmann/json/tree/develop/doc/examples) can be compiled and executed on their own (e.g., file [emplace.cpp](https://github.com/nlohmann/json/blob/develop/doc/examples/emplace.cpp)).
+
+### JSON as first-class data type
+
Here are some examples to give you an idea how to use the class.
Assume you want to create the JSON object
@@ -123,12 +158,14 @@ json empty_object_implicit = json({});
json empty_object_explicit = json::object();
// a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]]
-json array_not_object = { json::array({"currency", "USD"}), json::array({"value", 42.99}) };
+json array_not_object = json::array({ {"currency", "USD"}, {"value", 42.99} });
```
### Serialization / Deserialization
+#### To/from strings
+
You can create an object (deserialization) by appending `_json` to a string literal:
```cpp
@@ -142,8 +179,14 @@ auto j2 = R"(
"pi": 3.141
}
)"_json;
+```
-// or explicitly
+Note that without appending the `_json` suffix, the passed string literal is not parsed, but just used as JSON string value. That is, `json j = "{ \"happy\": true, \"pi\": 3.141 }"` would just store the string `"{ "happy": true, "pi": 3.141 }"` rather than parsing the actual object.
+
+The above example can also be expressed explicitly using `json::parse()`:
+
+```cpp
+// parse explicitly
auto j3 = json::parse("{ \"happy\": true, \"pi\": 3.141 }");
```
@@ -162,6 +205,31 @@ std::cout << j.dump(4) << std::endl;
// }
```
+Note the difference between serialization and assignment:
+
+```cpp
+// store a string in a JSON value
+json j_string = "this is a string";
+
+// retrieve the string value (implicit JSON to std::string conversion)
+std::string cpp_string = j_string;
+// retrieve the string value (explicit JSON to std::string conversion)
+auto cpp_string2 = j_string.get<std::string>();
+
+// retrieve the serialized value (explicit JSON serialization)
+std::string serialized_string = j_string.dump();
+
+// output of original string
+std::cout << cpp_string << " == " << cpp_string2 << " == " << j_string.get<std::string>() << '\n';
+// output of serialized value
+std::cout << j_string << " == " << serialized_string << std::endl;
+```
+
+`.dump()` always returns the serialized value, and `.get<std::string>()` returns the originally stored string value.
+
+
+#### To/from streams (e.g. files, string streams)
+
You can also use streams to serialize and deserialize:
```cpp
@@ -176,10 +244,37 @@ std::cout << j;
std::cout << std::setw(4) << j << std::endl;
```
-These operators work for any subclasses of `std::istream` or `std::ostream`.
+These operators work for any subclasses of `std::istream` or `std::ostream`. Here is the same example with files:
+
+```cpp
+// read a JSON file
+std::ifstream i("file.json");
+json j;
+i >> j;
+
+// write prettified JSON to another file
+std::ofstream o("pretty.json");
+o << std::setw(4) << j << std::endl;
+```
Please note that setting the exception bit for `failbit` is inappropriate for this use case. It will result in program termination due to the `noexcept` specifier in use.
+#### Read from iterator range
+
+You can also read JSON from an iterator range; that is, from any container accessible by iterators whose content is stored as contiguous byte sequence, for instance a `std::vector<std::uint8_t>`:
+
+```cpp
+std::vector<std::uint8_t> v = {'t', 'r', 'u', 'e'};
+json j = json::parse(v.begin(), v.end());
+```
+
+You may leave the iterators for the range [begin, end):
+
+```cpp
+std::vector<std::uint8_t> v = {'t', 'r', 'u', 'e'};
+json j = json::parse(v);
+```
+
### STL-like access
@@ -192,6 +287,9 @@ j.push_back("foo");
j.push_back(1);
j.push_back(true);
+// also use emplace_back
+j.emplace_back(1.78);
+
// iterate the array
for (json::iterator it = j.begin(); it != j.end(); ++it) {
std::cout << *it << '\n';
@@ -207,6 +305,9 @@ const std::string tmp = j[0];
j[1] = 42;
bool foo = j.at(2);
+// comparison
+j == "[\"foo\", 1, true]"_json; // true
+
// other stuff
j.size(); // 3 entries
j.empty(); // false
@@ -221,15 +322,15 @@ j.is_object();
j.is_array();
j.is_string();
-// comparison
-j == "[\"foo\", 1, true]"_json; // true
-
// create an object
json o;
o["foo"] = 23;
o["bar"] = false;
o["baz"] = 3.141;
+// also use emplace
+o.emplace("weather", "sunny");
+
// special iterator member functions for objects
for (json::iterator it = o.begin(); it != o.end(); ++it) {
std::cout << it.key() << " : " << it.value() << "\n";
@@ -251,7 +352,7 @@ o.erase("foo");
### Conversion from STL containers
-Any sequence container (`std::array`, `std::vector`, `std::deque`, `std::forward_list`, `std::list`) whose values can be used to construct JSON types (e.g., integers, floating point numbers, Booleans, string types, or again STL containers described in this section) can be used to create a JSON array. The same holds for similar associative containers (`std::set`, `std::multiset`, `std::unordered_set`, `std::unordered_multiset`), but in these cases the order of the elements of the array depends how the elements are ordered in the respective STL container.
+Any sequence container (`std::array`, `std::vector`, `std::deque`, `std::forward_list`, `std::list`) whose values can be used to construct JSON types (e.g., integers, floating point numbers, Booleans, string types, or again STL containers described in this section) can be used to create a JSON array. The same holds for similar associative containers (`std::set`, `std::multiset`, `std::unordered_set`, `std::unordered_multiset`), but in these cases the order of the elements of the array depends on how the elements are ordered in the respective STL container.
```cpp
std::vector<int> c_vector {1, 2, 3, 4};
@@ -291,7 +392,7 @@ json j_umset(c_umset); // both entries for "one" are used
// maybe ["one", "two", "one", "four"]
```
-Likewise, any associative key-value containers (`std::map`, `std::multimap`, `std::unordered_map`, `std::unordered_multimap`) whose keys can construct an `std::string` and whose values can be used to construct JSON types (see examples above) can be used to to create a JSON object. Note that in case of multimaps only one key is used in the JSON object and the value depends on the internal order of the STL container.
+Likewise, any associative key-value containers (`std::map`, `std::multimap`, `std::unordered_map`, `std::unordered_multimap`) whose keys can construct an `std::string` and whose values can be used to construct JSON types (see examples above) can be used to create a JSON object. Note that in case of multimaps only one key is used in the JSON object and the value depends on the internal order of the STL container.
```cpp
std::map<std::string, int> c_map { {"one", 1}, {"two", 2}, {"three", 3} };
@@ -383,14 +484,266 @@ int vi = jn.get<int>();
// etc.
```
+### Arbitrary types conversions
+
+Every type can be serialized in JSON, not just STL-containers and scalar types. Usually, you would do something along those lines:
+
+```cpp
+namespace ns {
+ // a simple struct to model a person
+ struct person {
+ std::string name;
+ std::string address;
+ int age;
+ };
+}
+
+ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};
+
+// convert to JSON: copy each value into the JSON object
+json j;
+j["name"] = p.name;
+j["address"] = p.address;
+j["age"] = p.age;
+
+// ...
+
+// convert from JSON: copy each value from the JSON object
+ns::person p {
+ j["name"].get<std::string>(),
+ j["address"].get<std::string>(),
+ j["age"].get<int>()
+};
+```
+
+It works, but that's quite a lot of boilerplate... Fortunately, there's a better way:
+
+```cpp
+// create a person
+ns::person p {"Ned Flanders", "744 Evergreen Terrace", 60};
+
+// conversion: person -> json
+json j = p;
+
+std::cout << j << std::endl;
+// {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"}
+
+// conversion: json -> person
+ns::person p2 = j;
+
+// that's it
+assert(p == p2);
+```
+
+#### Basic usage
+
+To make this work with one of your types, you only need to provide two functions:
+
+```cpp
+using nlohmann::json;
+
+namespace ns {
+ void to_json(json& j, const person& p) {
+ j = json{{"name", p.name}, {"address", p.address}, {"age", p.age}};
+ }
+
+ void from_json(const json& j, person& p) {
+ p.name = j.at("name").get<std::string>();
+ p.address = j.at("address").get<std::string>();
+ p.age = j.at("age").get<int>();
+ }
+} // namespace ns
+```
+
+That's all! When calling the `json` constructor with your type, your custom `to_json` method will be automatically called.
+Likewise, when calling `get<your_type>()`, the `from_json` method will be called.
+
+Some important things:
+
+* Those methods **MUST** be in your type's namespace (which can be the global namespace), or the library will not be able to locate them (in this example, they are in namespace `ns`, where `person` is defined).
+* When using `get<your_type>()`, `your_type` **MUST** be [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). (There is a way to bypass this requirement described later.)
+* In function `from_json`, use function [`at()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a93403e803947b86f4da2d1fb3345cf2c.html#a93403e803947b86f4da2d1fb3345cf2c) to access the object values rather than `operator[]`. In case a key does not exist, `at` throws an exception that you can handle, whereas `operator[]` exhibits undefined behavior.
+* In case your type contains several `operator=` definitions, code like `your_variable = your_json;` [may not compile](https://github.com/nlohmann/json/issues/667). You need to write `your_variable = your_json.get<decltype your_variable>();` instead.
+* You do not need to add serializers or deserializers for STL types like `std::vector`: the library already implements these.
+* Be careful with the definition order of the `from_json`/`to_json` functions: If a type `B` has a member of type `A`, you **MUST** define `to_json(A)` before `to_json(B)`. Look at [issue 561](https://github.com/nlohmann/json/issues/561) for more details.
+
+
+#### How do I convert third-party types?
+
+This requires a bit more advanced technique. But first, let's see how this conversion mechanism works:
+
+The library uses **JSON Serializers** to convert types to json.
+The default serializer for `nlohmann::json` is `nlohmann::adl_serializer` (ADL means [Argument-Dependent Lookup](http://en.cppreference.com/w/cpp/language/adl)).
+
+It is implemented like this (simplified):
+
+```cpp
+template <typename T>
+struct adl_serializer {
+ static void to_json(json& j, const T& value) {
+ // calls the "to_json" method in T's namespace
+ }
+
+ static void from_json(const json& j, T& value) {
+ // same thing, but with the "from_json" method
+ }
+};
+```
+
+This serializer works fine when you have control over the type's namespace. However, what about `boost::optional`, or `std::filesystem::path` (C++17)? Hijacking the `boost` namespace is pretty bad, and it's illegal to add something other than template specializations to `std`...
+
+To solve this, you need to add a specialization of `adl_serializer` to the `nlohmann` namespace, here's an example:
+
+```cpp
+// partial specialization (full specialization works too)
+namespace nlohmann {
+ template <typename T>
+ struct adl_serializer<boost::optional<T>> {
+ static void to_json(json& j, const boost::optional<T>& opt) {
+ if (opt == boost::none) {
+ j = nullptr;
+ } else {
+ j = *opt; // this will call adl_serializer<T>::to_json which will
+ // find the free function to_json in T's namespace!
+ }
+ }
+
+ static void from_json(const json& j, boost::optional<T>& opt) {
+ if (j.is_null()) {
+ opt = boost::none;
+ } else {
+ opt = j.get<T>(); // same as above, but with
+ // adl_serializer<T>::from_json
+ }
+ }
+ };
+}
+```
+
+#### How can I use `get()` for non-default constructible/non-copyable types?
+
+There is a way, if your type is [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible). You will need to specialize the `adl_serializer` as well, but with a special `from_json` overload:
+
+```cpp
+struct move_only_type {
+ move_only_type() = delete;
+ move_only_type(int ii): i(ii) {}
+ move_only_type(const move_only_type&) = delete;
+ move_only_type(move_only_type&&) = default;
+
+ int i;
+};
+
+namespace nlohmann {
+ template <>
+ struct adl_serializer<move_only_type> {
+ // note: the return type is no longer 'void', and the method only takes
+ // one argument
+ static move_only_type from_json(const json& j) {
+ return {j.get<int>()};
+ }
+
+ // Here's the catch! You must provide a to_json method! Otherwise you
+ // will not be able to convert move_only_type to json, since you fully
+ // specialized adl_serializer on that type
+ static void to_json(json& j, move_only_type t) {
+ j = t.i;
+ }
+ };
+}
+```
+
+#### Can I write my own serializer? (Advanced use)
+
+Yes. You might want to take a look at [`unit-udt.cpp`](https://github.com/nlohmann/json/blob/develop/test/src/unit-udt.cpp) in the test suite, to see a few examples.
+
+If you write your own serializer, you'll need to do a few things:
+
+* use a different `basic_json` alias than `nlohmann::json` (the last template parameter of `basic_json` is the `JSONSerializer`)
+* use your `basic_json` alias (or a template parameter) in all your `to_json`/`from_json` methods
+* use `nlohmann::to_json` and `nlohmann::from_json` when you need ADL
+
+Here is an example, without simplifications, that only accepts types with a size <= 32, and uses ADL.
+
+```cpp
+// You should use void as a second template argument
+// if you don't need compile-time checks on T
+template<typename T, typename SFINAE = typename std::enable_if<sizeof(T) <= 32>::type>
+struct less_than_32_serializer {
+ template <typename BasicJsonType>
+ static void to_json(BasicJsonType& j, T value) {
+ // we want to use ADL, and call the correct to_json overload
+ using nlohmann::to_json; // this method is called by adl_serializer,
+ // this is where the magic happens
+ to_json(j, value);
+ }
+
+ template <typename BasicJsonType>
+ static void from_json(const BasicJsonType& j, T& value) {
+ // same thing here
+ using nlohmann::from_json;
+ from_json(j, value);
+ }
+};
+```
+
+Be **very** careful when reimplementing your serializer, you can stack overflow if you don't pay attention:
+
+```cpp
+template <typename T, void>
+struct bad_serializer
+{
+ template <typename BasicJsonType>
+ static void to_json(BasicJsonType& j, const T& value) {
+ // this calls BasicJsonType::json_serializer<T>::to_json(j, value);
+ // if BasicJsonType::json_serializer == bad_serializer ... oops!
+ j = value;
+ }
+
+ template <typename BasicJsonType>
+ static void to_json(const BasicJsonType& j, T& value) {
+ // this calls BasicJsonType::json_serializer<T>::from_json(j, value);
+ // if BasicJsonType::json_serializer == bad_serializer ... oops!
+ value = j.template get<T>(); // oops!
+ }
+};
+```
+
+### Binary formats (CBOR and MessagePack)
+
+Though JSON is a ubiquitous data format, it is not a very compact format suitable for data exchange, for instance over a network. Hence, the library supports [CBOR](http://cbor.io) (Concise Binary Object Representation) and [MessagePack](http://msgpack.org) to efficiently encode JSON values to byte vectors and to decode such vectors.
+
+```cpp
+// create a JSON value
+json j = R"({"compact": true, "schema": 0})"_json;
+
+// serialize to CBOR
+std::vector<std::uint8_t> v_cbor = json::to_cbor(j);
+
+// 0xa2, 0x67, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0xf5, 0x66, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x00
+
+// roundtrip
+json j_from_cbor = json::from_cbor(v_cbor);
+
+// serialize to MessagePack
+std::vector<std::uint8_t> v_msgpack = json::to_msgpack(j);
+
+// 0x82, 0xa7, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0xc3, 0xa6, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x00
+
+// roundtrip
+json j_from_msgpack = json::from_msgpack(v_msgpack);
+```
+
## Supported compilers
-Though it's 2016 already, the support for C++11 is still a bit sparse. Currently, the following compilers are known to work:
+Though it's 2017 already, the support for C++11 is still a bit sparse. Currently, the following compilers are known to work:
-- GCC 4.9 - 6.0 (and possibly later)
-- Clang 3.4 - 3.9 (and possibly later)
+- GCC 4.9 - 7.2 (and possibly later)
+- Clang 3.4 - 5.0 (and possibly later)
+- Intel C++ Compiler 17.0.2 (and possibly later)
- Microsoft Visual C++ 2015 / Build Tools 14.0.25123.0 (and possibly later)
+- Microsoft Visual C++ 2017 / Build Tools 15.5.180.51428 (and possibly later)
I would be happy to learn about other compilers/versions.
@@ -413,26 +766,28 @@ The following compilers are currently used in continuous integration at [Travis]
| Compiler | Operating System | Version String |
|-----------------|------------------------------|----------------|
-| GCC 4.9.3 | Ubuntu 14.04.4 LTS | g++-4.9 (Ubuntu 4.9.3-8ubuntu2~14.04) 4.9.3 |
-| GCC 5.3.0 | Ubuntu 14.04.4 LTS | g++-5 (Ubuntu 5.3.0-3ubuntu1~14.04) 5.3.0 20151204 |
-| GCC 6.1.1 | Ubuntu 14.04.4 LTS | g++-6 (Ubuntu 6.1.1-3ubuntu11~14.04.1) 6.1.1 20160511 |
-| Clang 3.6.0 | Ubuntu 14.04.4 LTS | clang version 3.6.0 (tags/RELEASE_360/final) |
-| Clang 3.6.1 | Ubuntu 14.04.4 LTS | clang version 3.6.1 (tags/RELEASE_361/final) |
-| Clang 3.6.2 | Ubuntu 14.04.4 LTS | clang version 3.6.2 (tags/RELEASE_362/final) |
-| Clang 3.7.0 | Ubuntu 14.04.4 LTS | clang version 3.7.0 (tags/RELEASE_370/final) |
-| Clang 3.7.1 | Ubuntu 14.04.4 LTS | clang version 3.7.1 (tags/RELEASE_371/final) |
-| Clang 3.8.0 | Ubuntu 14.04.4 LTS | clang version 3.8.0 (tags/RELEASE_380/final) |
-| Clang 3.8.1 | Ubuntu 14.04.4 LTS | clang version 3.8.1 (tags/RELEASE_381/final) |
-| Clang Xcode 6.1 | Darwin Kernel Version 13.4.0 (OSX 10.9.5) | Apple LLVM version 6.0 (clang-600.0.54) (based on LLVM 3.5svn) |
-| Clang Xcode 6.2 | Darwin Kernel Version 13.4.0 (OSX 10.9.5) | Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn) |
-| Clang Xcode 6.3 | Darwin Kernel Version 14.3.0 (OSX 10.10.3) | Apple LLVM version 6.1.0 (clang-602.0.49) (based on LLVM 3.6.0svn) |
+| GCC 4.9.4 | Ubuntu 14.04.5 LTS | g++-4.9 (Ubuntu 4.9.4-2ubuntu1~14.04.1) 4.9.4 |
+| GCC 5.4.1 | Ubuntu 14.04.5 LTS | g++-5 (Ubuntu 5.4.1-2ubuntu1~14.04) 5.4.1 20160904 |
+| GCC 6.3.0 | Ubuntu 14.04.5 LTS | g++-6 (Ubuntu/Linaro 6.3.0-18ubuntu2~14.04) 6.3.0 20170519 |
+| GCC 7.1.0 | Ubuntu 14.04.5 LTS | g++-7 (Ubuntu 7.1.0-5ubuntu2~14.04) 7.1.0
+| Clang 3.5.0 | Ubuntu 14.04.5 LTS | clang version 3.5.0-4ubuntu2~trusty2 (tags/RELEASE_350/final) |
+| Clang 3.6.2 | Ubuntu 14.04.5 LTS | clang version 3.6.2-svn240577-1~exp1 (branches/release_36) |
+| Clang 3.7.1 | Ubuntu 14.04.5 LTS | clang version 3.7.1-svn253571-1~exp1 (branches/release_37) |
+| Clang 3.8.0 | Ubuntu 14.04.5 LTS | clang version 3.8.0-2ubuntu3~trusty5 (tags/RELEASE_380/final) |
+| Clang 3.9.1 | Ubuntu 14.04.5 LTS | clang version 3.9.1-4ubuntu3~14.04.2 (tags/RELEASE_391/rc2) |
+| Clang 4.0.1 | Ubuntu 14.04.5 LTS | clang version 4.0.1-svn305264-1~exp1 (branches/release_40) |
+| Clang 5.0.0 | Ubuntu 14.04.5 LTS | clang version 5.0.0-svn310902-1~exp1 (branches/release_50) |
| Clang Xcode 6.4 | Darwin Kernel Version 14.3.0 (OSX 10.10.3) | Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn) |
-| Clang Xcode 7.1 | Darwin Kernel Version 14.5.0 (OSX 10.10.5) | Apple LLVM version 7.0.0 (clang-700.1.76) |
-| Clang Xcode 7.2 | Darwin Kernel Version 15.0.0 (OSX 10.10.5) | Apple LLVM version 7.0.2 (clang-700.1.81) |
| Clang Xcode 7.3 | Darwin Kernel Version 15.0.0 (OSX 10.10.5) | Apple LLVM version 7.3.0 (clang-703.0.29) |
-| Clang Xcode 8.0 | Darwin Kernel Version 15.6.0 (OSX 10.11.6) | Apple LLVM version 8.0.0 (clang-800.0.38) |
-| Visual Studio 14 2015 | Windows Server 2012 R2 (x64) | Microsoft (R) Build Engine version 14.0.25123.0 |
-
+| Clang Xcode 8.0 | Darwin Kernel Version 15.6.0 | Apple LLVM version 8.0.0 (clang-800.0.38) |
+| Clang Xcode 8.1 | Darwin Kernel Version 16.1.0 (macOS 10.12.1) | Apple LLVM version 8.0.0 (clang-800.0.42.1) |
+| Clang Xcode 8.2 | Darwin Kernel Version 16.1.0 (macOS 10.12.1) | Apple LLVM version 8.0.0 (clang-800.0.42.1) |
+| Clang Xcode 8.3 | Darwin Kernel Version 16.5.0 (macOS 10.12.4) | Apple LLVM version 8.1.0 (clang-802.0.38) |
+| Clang Xcode 9.0 | Darwin Kernel Version 16.7.0 (macOS 10.12.6) | Apple LLVM version 9.0.0 (clang-900.0.37) |
+| Clang Xcode 9.1 | Darwin Kernel Version 16.7.0 (macOS 10.12.6) | Apple LLVM version 9.0.0 (clang-900.0.38) |
+| Clang Xcode 9.2 | Darwin Kernel Version 16.7.0 (macOS 10.12.6) | Apple LLVM version 8.1.0 (clang-900.0.39.2) |
+| Visual Studio 14 2015 | Windows Server 2012 R2 (x64) | Microsoft (R) Build Engine version 14.0.25420.1, MSVC 19.0.24215.1 |
+| Visual Studio 2017 | Windows Server 2016 | Microsoft (R) Build Engine version 15.5.180.51428, MSVC 19.12.25830.2 |
## License
@@ -440,7 +795,7 @@ The following compilers are currently used in continuous integration at [Travis]
The class is licensed under the [MIT License](http://opensource.org/licenses/MIT):
-Copyright &copy; 2013-2016 [Niels Lohmann](http://nlohmann.me)
+Copyright &copy; 2013-2017 [Niels Lohmann](http://nlohmann.me)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
@@ -448,11 +803,23 @@ The above copyright notice and this permission notice shall be included in all c
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+* * *
+
+The class contains the UTF-8 Decoder from Bjoern Hoehrmann which is licensed under the [MIT License](http://opensource.org/licenses/MIT) (see above). Copyright &copy; 2008-2009 [Björn Hoehrmann](http://bjoern.hoehrmann.de/) <bjoern@hoehrmann.de>
+
+## Contact
+
+If you have questions regarding the library, I would like to invite you to [open an issue at Github](https://github.com/nlohmann/json/issues/new). Please describe your request, problem, or question as detailed as possible, and also mention the version of the library you are using as well as the version of your compiler and operating system. Opening an issue at Github allows other users and contributors to this library to collaborate. For instance, I have little experience with MSVC, and most issues in this regard have been solved by a growing community. If you have a look at the [closed issues](https://github.com/nlohmann/json/issues?q=is%3Aissue+is%3Aclosed), you will see that we react quite timely in most cases.
+
+Only if your request would contain confidential information, please [send me an email](mailto:mail@nlohmann.me). For encrypted messages, please use [this key](https://keybase.io/nlohmann/pgp_keys.asc).
+
## Thanks
I deeply appreciate the help of the following people.
+![Contributors](https://raw.githubusercontent.com/nlohmann/json/develop/doc/avatars.png)
+
- [Teemperor](https://github.com/Teemperor) implemented CMake support and lcov integration, realized escape and Unicode handling in the string parser, and fixed the JSON serialization.
- [elliotgoodrich](https://github.com/elliotgoodrich) fixed an issue with double deletion in the iterator classes.
- [kirkshoop](https://github.com/kirkshoop) made the iterators of the class composable to other libraries.
@@ -465,7 +832,7 @@ I deeply appreciate the help of the following people.
- [Eric Cornelius](https://github.com/EricMCornelius) pointed out a bug in the handling with NaN and infinity values. He also improved the performance of the string escaping.
- [易思龙](https://github.com/likebeta) implemented a conversion from anonymous enums.
- [kepkin](https://github.com/kepkin) patiently pushed forward the support for Microsoft Visual studio.
-- [gregmarr](https://github.com/gregmarr) simplified the implementation of reverse iterators and helped with numerous hints and improvements.
+- [gregmarr](https://github.com/gregmarr) simplified the implementation of reverse iterators and helped with numerous hints and improvements. In particular, he pushed forward the implementation of user-defined types.
- [Caio Luppi](https://github.com/caiovlp) fixed a bug in the Unicode handling.
- [dariomt](https://github.com/dariomt) fixed some typos in the examples.
- [Daniel Frey](https://github.com/d-frey) cleaned up some pointers and implemented exception-safe memory allocation.
@@ -493,14 +860,98 @@ I deeply appreciate the help of the following people.
- [duncanwerner](https://github.com/duncanwerner) found a really embarrassing performance regression in the 2.0.0 release.
- [Damien](https://github.com/dtoma) fixed one of the last conversion warnings.
- [Thomas Braun](https://github.com/t-b) fixed a warning in a test case.
-- [Théo DELRIEU](https://github.com/theodelrieu) patiently and constructively oversaw the long way toward [iterator-range parsing](https://github.com/nlohmann/json/issues/290).
+- [Théo DELRIEU](https://github.com/theodelrieu) patiently and constructively oversaw the long way toward [iterator-range parsing](https://github.com/nlohmann/json/issues/290). He also implemented the magic behind the serialization/deserialization of user-defined types.
- [Stefan](https://github.com/5tefan) fixed a minor issue in the documentation.
- [Vasil Dimov](https://github.com/vasild) fixed the documentation regarding conversions from `std::multiset`.
- [ChristophJud](https://github.com/ChristophJud) overworked the CMake files to ease project inclusion.
-- [Vladimir Petrigo](https://github.com/vpetrigo) made a SFINAE hack more readable.
+- [Vladimir Petrigo](https://github.com/vpetrigo) made a SFINAE hack more readable and added Visual Studio 17 to the build matrix.
- [Denis Andrejew](https://github.com/seeekr) fixed a grammar issue in the README file.
-
-Thanks a lot for helping out!
+- [Pierre-Antoine Lacaze](https://github.com/palacaze) found a subtle bug in the `dump()` function.
+- [TurpentineDistillery](https://github.com/TurpentineDistillery) pointed to [`std::locale::classic()`](http://en.cppreference.com/w/cpp/locale/locale/classic) to avoid too much locale joggling, found some nice performance improvements in the parser, improved the benchmarking code, and realized locale-independent number parsing and printing.
+- [cgzones](https://github.com/cgzones) had an idea how to fix the Coverity scan.
+- [Jared Grubb](https://github.com/jaredgrubb) silenced a nasty documentation warning.
+- [Yixin Zhang](https://github.com/qwename) fixed an integer overflow check.
+- [Bosswestfalen](https://github.com/Bosswestfalen) merged two iterator classes into a smaller one.
+- [Daniel599](https://github.com/Daniel599) helped to get Travis execute the tests with Clang's sanitizers.
+- [Jonathan Lee](https://github.com/vjon) fixed an example in the README file.
+- [gnzlbg](https://github.com/gnzlbg) supported the implementation of user-defined types.
+- [Alexej Harm](https://github.com/qis) helped to get the user-defined types working with Visual Studio.
+- [Jared Grubb](https://github.com/jaredgrubb) supported the implementation of user-defined types.
+- [EnricoBilla](https://github.com/EnricoBilla) noted a typo in an example.
+- [Martin Hořeňovský](https://github.com/horenmar) found a way for a 2x speedup for the compilation time of the test suite.
+- [ukhegg](https://github.com/ukhegg) found proposed an improvement for the examples section.
+- [rswanson-ihi](https://github.com/rswanson-ihi) noted a typo in the README.
+- [Mihai Stan](https://github.com/stanmihai4) fixed a bug in the comparison with `nullptr`s.
+- [Tushar Maheshwari](https://github.com/tusharpm) added [cotire](https://github.com/sakra/cotire) support to speed up the compilation.
+- [TedLyngmo](https://github.com/TedLyngmo) noted a typo in the README, removed unnecessary bit arithmetic, and fixed some `-Weffc++` warnings.
+- [Krzysztof Woś](https://github.com/krzysztofwos) made exceptions more visible.
+- [ftillier](https://github.com/ftillier) fixed a compiler warning.
+- [tinloaf](https://github.com/tinloaf) made sure all pushed warnings are properly popped.
+- [Fytch](https://github.com/Fytch) found a bug in the documentation.
+- [Jay Sistar](https://github.com/Type1J) implemented a Meson build description.
+- [Henry Lee](https://github.com/HenryRLee) fixed a warning in ICC and improved the iterator implementation.
+- [Vincent Thiery](https://github.com/vthiery) maintains a package for the Conan package manager.
+- [Steffen](https://github.com/koemeet) fixed a potential issue with MSVC and `std::min`.
+- [Mike Tzou](https://github.com/Chocobo1) fixed some typos.
+- [amrcode](https://github.com/amrcode) noted a missleading documentation about comparison of floats.
+- [Oleg Endo](https://github.com/olegendo) reduced the memory consumption by replacing `<iostream>` with `<iosfwd>`.
+- [dan-42](https://github.com/dan-42) cleaned up the CMake files to simplify including/reusing of the library.
+- [Nikita Ofitserov](https://github.com/himikof) allowed for moving values from initializer lists.
+- [Greg Hurrell](https://github.com/wincent) fixed a typo.
+- [Dmitry Kukovinets](https://github.com/DmitryKuk) fixed a typo.
+- [kbthomp1](https://github.com/kbthomp1) fixed an issue related to the Intel OSX compiler.
+- [Markus Werle](https://github.com/daixtrose) fixed a typo.
+- [WebProdPP](https://github.com/WebProdPP) fixed a subtle error in a precondition check.
+- [Alex](https://github.com/leha-bot) noted an error in a code sample.
+- [Tom de Geus](https://github.com/tdegeus) reported some warnings with ICC and helped fixing them.
+- [Perry Kundert](https://github.com/pjkundert) simplified reading from input streams.
+- [Sonu Lohani](https://github.com/sonulohani) fixed a small compilation error.
+- [Jamie Seward](https://github.com/jseward) fixed all MSVC warnings.
+- [Nate Vargas](https://github.com/eld00d) added a Doxygen tag file.
+- [pvleuven](https://github.com/pvleuven) helped fixing a warning in ICC.
+- [Pavel](https://github.com/crea7or) helped fixing some warnings in MSVC.
+- [Jamie Seward](https://github.com/jseward) avoided unneccessary string copies in `find()` and `count()`.
+- [Mitja](https://github.com/Itja) fixed some typos.
+- [Jorrit Wronski](https://github.com/jowr) updated the Hunter package links.
+- [Matthias Möller](https://github.com/TinyTinni) added a `.natvis` for the MSVC debug view.
+- [bogemic](https://github.com/bogemic) fixed some C++17 deprecation warnings.
+- [Eren Okka](https://github.com/erengy) fixed some MSVC warnings.
+
+
+Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone.
+
+
+## Used third-party tools
+
+The library itself contains of a single header file licensed under the MIT license. However, it is built, tested, documented, and whatnot using a lot of third-party tools and services. Thanks a lot!
+
+- [**American fuzzy lop**](http://lcamtuf.coredump.cx/afl/) for fuzz testing
+- [**AppVeyor**](https://www.appveyor.com) for [continuous integration](https://ci.appveyor.com/project/nlohmann/json) on Windows
+- [**Artistic Style**](http://astyle.sourceforge.net) for automatic source code identation
+- [**benchpress**](https://github.com/sbs-ableton/benchpress) to benchmark the code
+- [**Catch**](https://github.com/philsquared/Catch) for the unit tests
+- [**Clang**](http://clang.llvm.org) for compilation with code sanitizers
+- [**Cmake**](https://cmake.org) for build automation
+- [**Codacity**](https://www.codacy.com) for further [code analysis](https://www.codacy.com/app/nlohmann/json)
+- [**Coveralls**](https://coveralls.io) to measure [code coverage](https://coveralls.io/github/nlohmann/json)
+- [**Coverity Scan**](https://scan.coverity.com) for [static analysis](https://scan.coverity.com/projects/nlohmann-json)
+- [**cppcheck**](http://cppcheck.sourceforge.net) for static analysis
+- [**cxxopts**](https://github.com/jarro2783/cxxopts) to let benchpress parse command-line parameters
+- [**Doxygen**](http://www.stack.nl/~dimitri/doxygen/) to generate [documentation](https://nlohmann.github.io/json/)
+- [**git-update-ghpages**](https://github.com/rstacruz/git-update-ghpages) to upload the documentation to gh-pages
+- [**Github Changelog Generator**](https://github.com/skywinder/github-changelog-generator) to generate the [ChangeLog](https://github.com/nlohmann/json/blob/develop/ChangeLog.md)
+- [**libFuzzer**](http://llvm.org/docs/LibFuzzer.html) to implement fuzz testing for OSS-Fuzz
+- [**OSS-Fuzz**](https://github.com/google/oss-fuzz) for continuous fuzz testing of the library
+- [**Probot**](https://probot.github.io) for automating maintainer tasks such as closing stale issues, requesting missing information, or detecting toxic comments.
+- [**send_to_wandbox**](https://github.com/nlohmann/json/blob/develop/doc/scripts/send_to_wandbox.py) to send code examples to [Wandbox](http://melpon.org/wandbox)
+- [**Travis**](https://travis-ci.org) for [continuous integration](https://travis-ci.org/nlohmann/json) on Linux and macOS
+- [**Valgrind**](http://valgrind.org) to check for correct memory management
+- [**Wandbox**](http://melpon.org/wandbox) for [online examples](https://wandbox.org/permlink/Op57X0V7fTf2tdwl)
+
+
+## Projects using JSON for Modern C++
+
+The library is currently used in Apple macOS Sierra and iOS 10. I am not sure what they are using the library for, but I am happy that it runs on so many devices.
## Notes
@@ -509,9 +960,13 @@ Thanks a lot for helping out!
- As the exact type of a number is not defined in the [JSON specification](http://rfc7159.net/rfc7159), this library tries to choose the best fitting C++ number type automatically. As a result, the type `double` may be used to store numbers which may yield [**floating-point exceptions**](https://github.com/nlohmann/json/issues/181) in certain rare situations if floating-point exceptions have been unmasked in the calling code. These exceptions are not caused by the library and need to be fixed in the calling code, such as by re-masking the exceptions prior to calling library functions.
- The library supports **Unicode input** as follows:
- Only **UTF-8** encoded input is supported which is the default encoding for JSON according to [RFC 7159](http://rfc7159.net/rfc7159#rfc.section.8.1).
- - Other encodings such as Latin-1, UTF-16, or UTF-32 are not supported and will yield parse errors.
+ - Other encodings such as Latin-1, UTF-16, or UTF-32 are not supported and will yield parse or serialization errors.
- [Unicode noncharacters](http://www.unicode.org/faq/private_use.html#nonchar1) will not be replaced by the library.
- Invalid surrogates (e.g., incomplete pairs such as `\uDEAD`) will yield parse errors.
+ - The strings stored in the library are UTF-8 encoded. When using the default string type (`std::string`), note that its length/size functions return the number of stored bytes rather than the number of characters or glyphs.
+- The code can be compiled without C++ **runtime type identification** features; that is, you can use the `-fno-rtti` compiler flag.
+- **Exceptions** are used widely within the library. They can, however, be switched off with either using the compiler flag `-fno-exceptions` or by defining the symbol `JSON_NOEXCEPTION`. In this case, exceptions are replaced by an `abort()` call.
+- By default, the library does not preserve the **insertion order of object elements**. This is standards-compliant, as the [JSON standard](https://tools.ietf.org/html/rfc7159.html) defines objects as "an unordered collection of zero or more name/value pairs". If you do want to preserve the insertion order, you can specialize the object type with containers like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) ([integration](https://github.com/nlohmann/json/issues/546#issuecomment-304447518)) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) ([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)).
## Execute unit tests
@@ -519,20 +974,11 @@ Thanks a lot for helping out!
To compile and run the tests, you need to execute
```sh
-$ make check
-
-===============================================================================
-All tests passed (8905491 assertions in 36 test cases)
-```
-
-Alternatively, you can use [CMake](https://cmake.org) and run
-
-```sh
$ mkdir build
$ cd build
$ cmake ..
-$ make
-$ ctest
+$ cmake --build .
+$ ctest --output-on-failure
```
For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml).
diff --git a/ext/json/json.hpp b/ext/json/json.hpp
index 9d48e7a6..5b0b0ea5 100644
--- a/ext/json/json.hpp
+++ b/ext/json/json.hpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++
-| | |__ | | | | | | version 2.0.10
+| | |__ | | | | | | version 3.0.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -29,42 +29,41 @@ SOFTWARE.
#ifndef NLOHMANN_JSON_HPP
#define NLOHMANN_JSON_HPP
-#include <algorithm> // all_of, for_each, transform
+#include <algorithm> // all_of, copy, fill, find, for_each, generate_n, none_of, remove, reverse, transform
#include <array> // array
#include <cassert> // assert
-#include <cctype> // isdigit
#include <ciso646> // and, not, or
-#include <cmath> // isfinite, ldexp, signbit
+#include <clocale> // lconv, localeconv
+#include <cmath> // isfinite, labs, ldexp, signbit
#include <cstddef> // nullptr_t, ptrdiff_t, size_t
#include <cstdint> // int64_t, uint64_t
-#include <cstdlib> // strtod, strtof, strtold, strtoul
-#include <cstring> // strlen
+#include <cstdlib> // abort, strtod, strtof, strtold, strtoul, strtoll, strtoull
+#include <cstring> // memcpy, strlen
+#include <forward_list> // forward_list
#include <functional> // function, hash, less
#include <initializer_list> // initializer_list
-#include <iomanip> // setw
-#include <iostream> // istream, ostream
-#include <iterator> // advance, begin, bidirectional_iterator_tag, distance, end, inserter, iterator, iterator_traits, next, random_access_iterator_tag, reverse_iterator
+#include <iomanip> // hex
+#include <iosfwd> // istream, ostream
+#include <iterator> // advance, begin, back_inserter, bidirectional_iterator_tag, distance, end, inserter, iterator, iterator_traits, next, random_access_iterator_tag, reverse_iterator
#include <limits> // numeric_limits
#include <locale> // locale
#include <map> // map
#include <memory> // addressof, allocator, allocator_traits, unique_ptr
#include <numeric> // accumulate
#include <sstream> // stringstream
-#include <stdexcept> // domain_error, invalid_argument, out_of_range
#include <string> // getline, stoi, string, to_string
-#include <type_traits> // add_pointer, enable_if, is_arithmetic, is_base_of, is_const, is_constructible, is_convertible, is_floating_point, is_integral, is_nothrow_move_assignable, std::is_nothrow_move_constructible, std::is_pointer, std::is_reference, std::is_same, remove_const, remove_pointer, remove_reference
+#include <type_traits> // add_pointer, conditional, decay, enable_if, false_type, integral_constant, is_arithmetic, is_base_of, is_const, is_constructible, is_convertible, is_default_constructible, is_enum, is_floating_point, is_integral, is_nothrow_move_assignable, is_nothrow_move_constructible, is_pointer, is_reference, is_same, is_scalar, is_signed, remove_const, remove_cv, remove_pointer, remove_reference, true_type, underlying_type
#include <utility> // declval, forward, make_pair, move, pair, swap
+#include <valarray> // valarray
#include <vector> // vector
// exclude unsupported compilers
#if defined(__clang__)
- #define CLANG_VERSION (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__)
- #if CLANG_VERSION < 30400
+ #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400
#error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers"
#endif
-#elif defined(__GNUC__)
- #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
- #if GCC_VERSION < 40900
+#elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER))
+ #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40900
#error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers"
#endif
#endif
@@ -90,6 +89,34 @@ SOFTWARE.
#define JSON_DEPRECATED
#endif
+// allow to disable exceptions
+#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && not defined(JSON_NOEXCEPTION)
+ #define JSON_THROW(exception) throw exception
+ #define JSON_TRY try
+ #define JSON_CATCH(exception) catch(exception)
+#else
+ #define JSON_THROW(exception) std::abort()
+ #define JSON_TRY if(true)
+ #define JSON_CATCH(exception) if(false)
+#endif
+
+// manual branch prediction
+#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
+ #define JSON_LIKELY(x) __builtin_expect(!!(x), 1)
+ #define JSON_UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+ #define JSON_LIKELY(x) x
+ #define JSON_UNLIKELY(x) x
+#endif
+
+// C++ language standard detection
+#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
+ #define JSON_HAS_CPP_17
+ #define JSON_HAS_CPP_14
+#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)
+ #define JSON_HAS_CPP_14
+#endif
+
/*!
@brief namespace for Niels Lohmann
@see https://github.com/nlohmann
@@ -97,39 +124,7163 @@ SOFTWARE.
*/
namespace nlohmann
{
+template<typename = void, typename = void>
+struct adl_serializer;
+
+// forward declaration of basic_json (required to split the class)
+template<template<typename, typename, typename...> class ObjectType = std::map,
+ template<typename, typename...> class ArrayType = std::vector,
+ class StringType = std::string, class BooleanType = bool,
+ class NumberIntegerType = std::int64_t,
+ class NumberUnsignedType = std::uint64_t,
+ class NumberFloatType = double,
+ template<typename> class AllocatorType = std::allocator,
+ template<typename, typename = void> class JSONSerializer = adl_serializer>
+class basic_json;
+
+// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
+// may be removed in the future once the class is split.
+
+#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \
+ template<template<typename, typename, typename...> class ObjectType, \
+ template<typename, typename...> class ArrayType, \
+ class StringType, class BooleanType, class NumberIntegerType, \
+ class NumberUnsignedType, class NumberFloatType, \
+ template<typename> class AllocatorType, \
+ template<typename, typename = void> class JSONSerializer>
+
+#define NLOHMANN_BASIC_JSON_TPL \
+ basic_json<ObjectType, ArrayType, StringType, BooleanType, \
+ NumberIntegerType, NumberUnsignedType, NumberFloatType, \
+ AllocatorType, JSONSerializer>
/*!
@brief unnamed namespace with internal helper functions
+
+This namespace collects some functions that could not be defined inside the
+@ref basic_json class.
+
+@since version 2.1.0
+*/
+namespace detail
+{
+////////////////
+// exceptions //
+////////////////
+
+/*!
+@brief general exception of the @ref basic_json class
+
+This class is an extension of `std::exception` objects with a member @a id for
+exception ids. It is used as the base class for all exceptions thrown by the
+@ref basic_json class. This class can hence be used as "wildcard" to catch
+exceptions.
+
+Subclasses:
+- @ref parse_error for exceptions indicating a parse error
+- @ref invalid_iterator for exceptions indicating errors with iterators
+- @ref type_error for exceptions indicating executing a member function with
+ a wrong type
+- @ref out_of_range for exceptions indicating access out of the defined range
+- @ref other_error for exceptions indicating other library errors
+
+@internal
+@note To have nothrow-copy-constructible exceptions, we internally use
+ `std::runtime_error` which can cope with arbitrary-length error messages.
+ Intermediate strings are built with static functions and then passed to
+ the actual constructor.
+@endinternal
+
+@liveexample{The following code shows how arbitrary library exceptions can be
+caught.,exception}
+
+@since version 3.0.0
+*/
+class exception : public std::exception
+{
+ public:
+ /// returns the explanatory string
+ const char* what() const noexcept override
+ {
+ return m.what();
+ }
+
+ /// the id of the exception
+ const int id;
+
+ protected:
+ exception(int id_, const char* what_arg) : id(id_), m(what_arg) {}
+
+ static std::string name(const std::string& ename, int id_)
+ {
+ return "[json.exception." + ename + "." + std::to_string(id_) + "] ";
+ }
+
+ private:
+ /// an exception object as storage for error messages
+ std::runtime_error m;
+};
+
+/*!
+@brief exception indicating a parse error
+
+This exception is thrown by the library when a parse error occurs. Parse errors
+can occur during the deserialization of JSON text, CBOR, MessagePack, as well
+as when using JSON Patch.
+
+Member @a byte holds the byte index of the last read character in the input
+file.
+
+Exceptions have ids 1xx.
+
+name / id | example message | description
+------------------------------ | --------------- | -------------------------
+json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position.
+json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point.
+json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid.
+json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects.
+json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors.
+json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`.
+json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character.
+json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences.
+json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number.
+json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read.
+json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read.
+json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read.
+
+@note For an input with n bytes, 1 is the index of the first character and n+1
+ is the index of the terminating null byte or the end of file. This also
+ holds true when reading a byte vector (CBOR or MessagePack).
+
+@liveexample{The following code shows how a `parse_error` exception can be
+caught.,parse_error}
+
+@sa @ref exception for the base class of the library exceptions
+@sa @ref invalid_iterator for exceptions indicating errors with iterators
+@sa @ref type_error for exceptions indicating executing a member function with
+ a wrong type
+@sa @ref out_of_range for exceptions indicating access out of the defined range
+@sa @ref other_error for exceptions indicating other library errors
+
+@since version 3.0.0
+*/
+class parse_error : public exception
+{
+ public:
+ /*!
+ @brief create a parse error exception
+ @param[in] id_ the id of the exception
+ @param[in] byte_ the byte index where the error occurred (or 0 if the
+ position cannot be determined)
+ @param[in] what_arg the explanatory string
+ @return parse_error object
+ */
+ static parse_error create(int id_, std::size_t byte_, const std::string& what_arg)
+ {
+ std::string w = exception::name("parse_error", id_) + "parse error" +
+ (byte_ != 0 ? (" at " + std::to_string(byte_)) : "") +
+ ": " + what_arg;
+ return parse_error(id_, byte_, w.c_str());
+ }
+
+ /*!
+ @brief byte index of the parse error
+
+ The byte index of the last read character in the input file.
+
+ @note For an input with n bytes, 1 is the index of the first character and
+ n+1 is the index of the terminating null byte or the end of file.
+ This also holds true when reading a byte vector (CBOR or MessagePack).
+ */
+ const std::size_t byte;
+
+ private:
+ parse_error(int id_, std::size_t byte_, const char* what_arg)
+ : exception(id_, what_arg), byte(byte_) {}
+};
+
+/*!
+@brief exception indicating errors with iterators
+
+This exception is thrown if iterators passed to a library function do not match
+the expected semantics.
+
+Exceptions have ids 2xx.
+
+name / id | example message | description
+----------------------------------- | --------------- | -------------------------
+json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.
+json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion.
+json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from.
+json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid.
+json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid.
+json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range.
+json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key.
+json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.
+json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.
+json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.
+json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to.
+json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container.
+json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered.
+json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin().
+
+@liveexample{The following code shows how an `invalid_iterator` exception can be
+caught.,invalid_iterator}
+
+@sa @ref exception for the base class of the library exceptions
+@sa @ref parse_error for exceptions indicating a parse error
+@sa @ref type_error for exceptions indicating executing a member function with
+ a wrong type
+@sa @ref out_of_range for exceptions indicating access out of the defined range
+@sa @ref other_error for exceptions indicating other library errors
+
+@since version 3.0.0
+*/
+class invalid_iterator : public exception
+{
+ public:
+ static invalid_iterator create(int id_, const std::string& what_arg)
+ {
+ std::string w = exception::name("invalid_iterator", id_) + what_arg;
+ return invalid_iterator(id_, w.c_str());
+ }
+
+ private:
+ invalid_iterator(int id_, const char* what_arg)
+ : exception(id_, what_arg) {}
+};
+
+/*!
+@brief exception indicating executing a member function with a wrong type
+
+This exception is thrown in case of a type error; that is, a library function is
+executed on a JSON value whose type does not match the expected semantics.
+
+Exceptions have ids 3xx.
+
+name / id | example message | description
+----------------------------- | --------------- | -------------------------
+json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead.
+json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types.
+json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t&.
+json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types.
+json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types.
+json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types.
+json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types.
+json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types.
+json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types.
+json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types.
+json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types.
+json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types.
+json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined.
+json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers.
+json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive.
+json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. |
+
+@liveexample{The following code shows how a `type_error` exception can be
+caught.,type_error}
+
+@sa @ref exception for the base class of the library exceptions
+@sa @ref parse_error for exceptions indicating a parse error
+@sa @ref invalid_iterator for exceptions indicating errors with iterators
+@sa @ref out_of_range for exceptions indicating access out of the defined range
+@sa @ref other_error for exceptions indicating other library errors
+
+@since version 3.0.0
+*/
+class type_error : public exception
+{
+ public:
+ static type_error create(int id_, const std::string& what_arg)
+ {
+ std::string w = exception::name("type_error", id_) + what_arg;
+ return type_error(id_, w.c_str());
+ }
+
+ private:
+ type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
+};
+
+/*!
+@brief exception indicating access out of the defined range
+
+This exception is thrown in case a library function is called on an input
+parameter that exceeds the expected range, for instance in case of array
+indices or nonexisting object keys.
+
+Exceptions have ids 4xx.
+
+name / id | example message | description
+------------------------------- | --------------- | -------------------------
+json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1.
+json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it.
+json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object.
+json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved.
+json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value.
+json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF.
+
+@liveexample{The following code shows how an `out_of_range` exception can be
+caught.,out_of_range}
+
+@sa @ref exception for the base class of the library exceptions
+@sa @ref parse_error for exceptions indicating a parse error
+@sa @ref invalid_iterator for exceptions indicating errors with iterators
+@sa @ref type_error for exceptions indicating executing a member function with
+ a wrong type
+@sa @ref other_error for exceptions indicating other library errors
+
+@since version 3.0.0
+*/
+class out_of_range : public exception
+{
+ public:
+ static out_of_range create(int id_, const std::string& what_arg)
+ {
+ std::string w = exception::name("out_of_range", id_) + what_arg;
+ return out_of_range(id_, w.c_str());
+ }
+
+ private:
+ out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}
+};
+
+/*!
+@brief exception indicating other library errors
+
+This exception is thrown in case of errors that cannot be classified with the
+other exception types.
+
+Exceptions have ids 5xx.
+
+name / id | example message | description
+------------------------------ | --------------- | -------------------------
+json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed.
+
+@sa @ref exception for the base class of the library exceptions
+@sa @ref parse_error for exceptions indicating a parse error
+@sa @ref invalid_iterator for exceptions indicating errors with iterators
+@sa @ref type_error for exceptions indicating executing a member function with
+ a wrong type
+@sa @ref out_of_range for exceptions indicating access out of the defined range
+
+@liveexample{The following code shows how an `other_error` exception can be
+caught.,other_error}
+
+@since version 3.0.0
+*/
+class other_error : public exception
+{
+ public:
+ static other_error create(int id_, const std::string& what_arg)
+ {
+ std::string w = exception::name("other_error", id_) + what_arg;
+ return other_error(id_, w.c_str());
+ }
+
+ private:
+ other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
+};
+
+
+
+///////////////////////////
+// JSON type enumeration //
+///////////////////////////
+
+/*!
+@brief the JSON type enumeration
+
+This enumeration collects the different JSON types. It is internally used to
+distinguish the stored values, and the functions @ref basic_json::is_null(),
+@ref basic_json::is_object(), @ref basic_json::is_array(),
+@ref basic_json::is_string(), @ref basic_json::is_boolean(),
+@ref basic_json::is_number() (with @ref basic_json::is_number_integer(),
+@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()),
+@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and
+@ref basic_json::is_structured() rely on it.
+
+@note There are three enumeration entries (number_integer, number_unsigned, and
+number_float), because the library distinguishes these three types for numbers:
+@ref basic_json::number_unsigned_t is used for unsigned integers,
+@ref basic_json::number_integer_t is used for signed integers, and
+@ref basic_json::number_float_t is used for floating-point numbers or to
+approximate integers which do not fit in the limits of their respective type.
+
+@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON
+value with the default value for a given type
+
@since version 1.0.0
*/
-namespace
+enum class value_t : uint8_t
+{
+ null, ///< null value
+ object, ///< object (unordered set of name/value pairs)
+ array, ///< array (ordered collection of values)
+ string, ///< string value
+ boolean, ///< boolean value
+ number_integer, ///< number value (signed integer)
+ number_unsigned, ///< number value (unsigned integer)
+ number_float, ///< number value (floating-point)
+ discarded ///< discarded by the the parser callback function
+};
+
+/*!
+@brief comparison operator for JSON types
+
+Returns an ordering that is similar to Python:
+- order: null < boolean < number < object < array < string
+- furthermore, each type is not smaller than itself
+- discarded values are not comparable
+
+@since version 1.0.0
+*/
+inline bool operator<(const value_t lhs, const value_t rhs) noexcept
{
+ static constexpr std::array<uint8_t, 8> order = {{
+ 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,
+ 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */
+ }
+ };
+
+ const auto l_index = static_cast<std::size_t>(lhs);
+ const auto r_index = static_cast<std::size_t>(rhs);
+ return l_index < order.size() and r_index < order.size() and order[l_index] < order[r_index];
+}
+
+
+/////////////
+// helpers //
+/////////////
+
+template<typename> struct is_basic_json : std::false_type {};
+
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+struct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};
+
+// alias templates to reduce boilerplate
+template<bool B, typename T = void>
+using enable_if_t = typename std::enable_if<B, T>::type;
+
+template<typename T>
+using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+
+// implementation of C++14 index_sequence and affiliates
+// source: https://stackoverflow.com/a/32223343
+template<std::size_t... Ints>
+struct index_sequence
+{
+ using type = index_sequence;
+ using value_type = std::size_t;
+ static constexpr std::size_t size() noexcept
+ {
+ return sizeof...(Ints);
+ }
+};
+
+template<class Sequence1, class Sequence2>
+struct merge_and_renumber;
+
+template<std::size_t... I1, std::size_t... I2>
+struct merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>>
+ : index_sequence < I1..., (sizeof...(I1) + I2)... > {};
+
+template<std::size_t N>
+struct make_index_sequence
+ : merge_and_renumber < typename make_index_sequence < N / 2 >::type,
+ typename make_index_sequence < N - N / 2 >::type > {};
+
+template<> struct make_index_sequence<0> : index_sequence<> {};
+template<> struct make_index_sequence<1> : index_sequence<0> {};
+
+template<typename... Ts>
+using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
+
+/*
+Implementation of two C++17 constructs: conjunction, negation. This is needed
+to avoid evaluating all the traits in a condition
+
+For example: not std::is_same<void, T>::value and has_value_type<T>::value
+will not compile when T = void (on MSVC at least). Whereas
+conjunction<negation<std::is_same<void, T>>, has_value_type<T>>::value will
+stop evaluating if negation<...>::value == false
+
+Please note that those constructs must be used with caution, since symbols can
+become very long quickly (which can slow down compilation and cause MSVC
+internal compiler errors). Only use it when you have to (see example ahead).
+*/
+template<class...> struct conjunction : std::true_type {};
+template<class B1> struct conjunction<B1> : B1 {};
+template<class B1, class... Bn>
+struct conjunction<B1, Bn...> : std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};
+
+template<class B> struct negation : std::integral_constant<bool, not B::value> {};
+
+// dispatch utility (taken from ranges-v3)
+template<unsigned N> struct priority_tag : priority_tag < N - 1 > {};
+template<> struct priority_tag<0> {};
+
+
+//////////////////
+// constructors //
+//////////////////
+
+template<value_t> struct external_constructor;
+
+template<>
+struct external_constructor<value_t::boolean>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
+ {
+ j.m_type = value_t::boolean;
+ j.m_value = b;
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::string>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
+ {
+ j.m_type = value_t::string;
+ j.m_value = s;
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
+ {
+ j.m_type = value_t::string;
+ j.m_value = std::move(s);
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::number_float>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
+ {
+ j.m_type = value_t::number_float;
+ j.m_value = val;
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::number_unsigned>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
+ {
+ j.m_type = value_t::number_unsigned;
+ j.m_value = val;
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::number_integer>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
+ {
+ j.m_type = value_t::number_integer;
+ j.m_value = val;
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::array>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
+ {
+ j.m_type = value_t::array;
+ j.m_value = arr;
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
+ {
+ j.m_type = value_t::array;
+ j.m_value = std::move(arr);
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType, typename CompatibleArrayType,
+ enable_if_t<not std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,
+ int> = 0>
+ static void construct(BasicJsonType& j, const CompatibleArrayType& arr)
+ {
+ using std::begin;
+ using std::end;
+ j.m_type = value_t::array;
+ j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, const std::vector<bool>& arr)
+ {
+ j.m_type = value_t::array;
+ j.m_value = value_t::array;
+ j.m_value.array->reserve(arr.size());
+ for (const bool x : arr)
+ {
+ j.m_value.array->push_back(x);
+ }
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType, typename T,
+ enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
+ static void construct(BasicJsonType& j, const std::valarray<T>& arr)
+ {
+ j.m_type = value_t::array;
+ j.m_value = value_t::array;
+ j.m_value.array->resize(arr.size());
+ std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin());
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::object>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
+ {
+ j.m_type = value_t::object;
+ j.m_value = obj;
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
+ {
+ j.m_type = value_t::object;
+ j.m_value = std::move(obj);
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType, typename CompatibleObjectType,
+ enable_if_t<not std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int> = 0>
+ static void construct(BasicJsonType& j, const CompatibleObjectType& obj)
+ {
+ using std::begin;
+ using std::end;
+
+ j.m_type = value_t::object;
+ j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
+ j.assert_invariant();
+ }
+};
+
+
+////////////////////////
+// has_/is_ functions //
+////////////////////////
+
/*!
@brief Helper to determine whether there's a key_type for T.
-Thus helper is used to tell associative containers apart from other containers
+This helper is used to tell associative containers apart from other containers
such as sequence containers. For instance, `std::map` passes the test as it
contains a `mapped_type`, whereas `std::vector` fails the test.
@sa http://stackoverflow.com/a/7728728/266378
@since version 1.0.0, overworked in version 2.0.6
*/
-template<typename T>
-struct has_mapped_type
+#define NLOHMANN_JSON_HAS_HELPER(type) \
+ template<typename T> struct has_##type { \
+ private: \
+ template<typename U, typename = typename U::type> \
+ static int detect(U &&); \
+ static void detect(...); \
+ public: \
+ static constexpr bool value = \
+ std::is_integral<decltype(detect(std::declval<T>()))>::value; \
+ }
+
+NLOHMANN_JSON_HAS_HELPER(mapped_type);
+NLOHMANN_JSON_HAS_HELPER(key_type);
+NLOHMANN_JSON_HAS_HELPER(value_type);
+NLOHMANN_JSON_HAS_HELPER(iterator);
+
+#undef NLOHMANN_JSON_HAS_HELPER
+
+
+template<bool B, class RealType, class CompatibleObjectType>
+struct is_compatible_object_type_impl : std::false_type {};
+
+template<class RealType, class CompatibleObjectType>
+struct is_compatible_object_type_impl<true, RealType, CompatibleObjectType>
+{
+ static constexpr auto value =
+ std::is_constructible<typename RealType::key_type, typename CompatibleObjectType::key_type>::value and
+ std::is_constructible<typename RealType::mapped_type, typename CompatibleObjectType::mapped_type>::value;
+};
+
+template<class BasicJsonType, class CompatibleObjectType>
+struct is_compatible_object_type
+{
+ static auto constexpr value = is_compatible_object_type_impl <
+ conjunction<negation<std::is_same<void, CompatibleObjectType>>,
+ has_mapped_type<CompatibleObjectType>,
+ has_key_type<CompatibleObjectType>>::value,
+ typename BasicJsonType::object_t, CompatibleObjectType >::value;
+};
+
+template<typename BasicJsonType, typename T>
+struct is_basic_json_nested_type
+{
+ static auto constexpr value = std::is_same<T, typename BasicJsonType::iterator>::value or
+ std::is_same<T, typename BasicJsonType::const_iterator>::value or
+ std::is_same<T, typename BasicJsonType::reverse_iterator>::value or
+ std::is_same<T, typename BasicJsonType::const_reverse_iterator>::value;
+};
+
+template<class BasicJsonType, class CompatibleArrayType>
+struct is_compatible_array_type
+{
+ static auto constexpr value =
+ conjunction<negation<std::is_same<void, CompatibleArrayType>>,
+ negation<is_compatible_object_type<
+ BasicJsonType, CompatibleArrayType>>,
+ negation<std::is_constructible<typename BasicJsonType::string_t,
+ CompatibleArrayType>>,
+ negation<is_basic_json_nested_type<BasicJsonType, CompatibleArrayType>>,
+ has_value_type<CompatibleArrayType>,
+ has_iterator<CompatibleArrayType>>::value;
+};
+
+template<bool, typename, typename>
+struct is_compatible_integer_type_impl : std::false_type {};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType>
+struct is_compatible_integer_type_impl<true, RealIntegerType, CompatibleNumberIntegerType>
+{
+ // is there an assert somewhere on overflows?
+ using RealLimits = std::numeric_limits<RealIntegerType>;
+ using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;
+
+ static constexpr auto value =
+ std::is_constructible<RealIntegerType, CompatibleNumberIntegerType>::value and
+ CompatibleLimits::is_integer and
+ RealLimits::is_signed == CompatibleLimits::is_signed;
+};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType>
+struct is_compatible_integer_type
+{
+ static constexpr auto value =
+ is_compatible_integer_type_impl <
+ std::is_integral<CompatibleNumberIntegerType>::value and
+ not std::is_same<bool, CompatibleNumberIntegerType>::value,
+ RealIntegerType, CompatibleNumberIntegerType >::value;
+};
+
+
+// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists
+template<typename BasicJsonType, typename T>
+struct has_from_json
{
private:
- template <typename U, typename = typename U::mapped_type>
+ // also check the return type of from_json
+ template<typename U, typename = enable_if_t<std::is_same<void, decltype(uncvref_t<U>::from_json(
+ std::declval<BasicJsonType>(), std::declval<T&>()))>::value>>
static int detect(U&&);
+ static void detect(...);
+
+ public:
+ static constexpr bool value = std::is_integral<decltype(
+ detect(std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
+};
+// This trait checks if JSONSerializer<T>::from_json(json const&) exists
+// this overload is used for non-default-constructible user-defined-types
+template<typename BasicJsonType, typename T>
+struct has_non_default_from_json
+{
+ private:
+ template<typename U, typename =
+ enable_if_t<std::is_same<T, decltype(uncvref_t<U>::from_json(std::declval<BasicJsonType>()))>::value>>
+ static int detect(U&&);
static void detect(...);
+
+ public:
+ static constexpr bool value = std::is_integral<decltype(detect(
+ std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
+};
+
+// This trait checks if BasicJsonType::json_serializer<T>::to_json exists
+template<typename BasicJsonType, typename T>
+struct has_to_json
+{
+ private:
+ template<typename U, typename = decltype(uncvref_t<U>::to_json(
+ std::declval<BasicJsonType&>(), std::declval<T>()))>
+ static int detect(U&&);
+ static void detect(...);
+
+ public:
+ static constexpr bool value = std::is_integral<decltype(detect(
+ std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
+};
+
+
+/////////////
+// to_json //
+/////////////
+
+template<typename BasicJsonType, typename T,
+ enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>
+void to_json(BasicJsonType& j, T b) noexcept
+{
+ external_constructor<value_t::boolean>::construct(j, b);
+}
+
+template<typename BasicJsonType, typename CompatibleString,
+ enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>
+void to_json(BasicJsonType& j, const CompatibleString& s)
+{
+ external_constructor<value_t::string>::construct(j, s);
+}
+
+template<typename BasicJsonType>
+void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)
+{
+ external_constructor<value_t::string>::construct(j, std::move(s));
+}
+
+template<typename BasicJsonType, typename FloatType,
+ enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>
+void to_json(BasicJsonType& j, FloatType val) noexcept
+{
+ external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));
+}
+
+template<typename BasicJsonType, typename CompatibleNumberUnsignedType,
+ enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>
+void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
+{
+ external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
+}
+
+template<typename BasicJsonType, typename CompatibleNumberIntegerType,
+ enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>
+void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
+{
+ external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
+}
+
+template<typename BasicJsonType, typename EnumType,
+ enable_if_t<std::is_enum<EnumType>::value, int> = 0>
+void to_json(BasicJsonType& j, EnumType e) noexcept
+{
+ using underlying_type = typename std::underlying_type<EnumType>::type;
+ external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e));
+}
+
+template<typename BasicJsonType>
+void to_json(BasicJsonType& j, const std::vector<bool>& e)
+{
+ external_constructor<value_t::array>::construct(j, e);
+}
+
+template<typename BasicJsonType, typename CompatibleArrayType,
+ enable_if_t<is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value or
+ std::is_same<typename BasicJsonType::array_t, CompatibleArrayType>::value,
+ int> = 0>
+void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
+{
+ external_constructor<value_t::array>::construct(j, arr);
+}
+
+template<typename BasicJsonType, typename T,
+ enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
+void to_json(BasicJsonType& j, std::valarray<T> arr)
+{
+ external_constructor<value_t::array>::construct(j, std::move(arr));
+}
+
+template<typename BasicJsonType>
+void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
+{
+ external_constructor<value_t::array>::construct(j, std::move(arr));
+}
+
+template<typename BasicJsonType, typename CompatibleObjectType,
+ enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value, int> = 0>
+void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
+{
+ external_constructor<value_t::object>::construct(j, obj);
+}
+
+template<typename BasicJsonType>
+void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
+{
+ external_constructor<value_t::object>::construct(j, std::move(obj));
+}
+
+template<typename BasicJsonType, typename T, std::size_t N,
+ enable_if_t<not std::is_constructible<typename BasicJsonType::string_t, T (&)[N]>::value, int> = 0>
+void to_json(BasicJsonType& j, T (&arr)[N])
+{
+ external_constructor<value_t::array>::construct(j, arr);
+}
+
+template<typename BasicJsonType, typename... Args>
+void to_json(BasicJsonType& j, const std::pair<Args...>& p)
+{
+ j = {p.first, p.second};
+}
+
+template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
+void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...>)
+{
+ j = {std::get<Idx>(t)...};
+}
+
+template<typename BasicJsonType, typename... Args>
+void to_json(BasicJsonType& j, const std::tuple<Args...>& t)
+{
+ to_json_tuple_impl(j, t, index_sequence_for<Args...> {});
+}
+
+///////////////
+// from_json //
+///////////////
+
+// overloads for basic_json template parameters
+template<typename BasicJsonType, typename ArithmeticType,
+ enable_if_t<std::is_arithmetic<ArithmeticType>::value and
+ not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
+ int> = 0>
+void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
+{
+ switch (static_cast<value_t>(j))
+ {
+ case value_t::number_unsigned:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
+ break;
+ }
+ case value_t::number_integer:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
+ break;
+ }
+ case value_t::number_float:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
+ break;
+ }
+
+ default:
+ JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name())));
+ }
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
+{
+ if (JSON_UNLIKELY(not j.is_boolean()))
+ {
+ JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name())));
+ }
+ b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
+{
+ if (JSON_UNLIKELY(not j.is_string()))
+ {
+ JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name())));
+ }
+ s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
+{
+ get_arithmetic_value(j, val);
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
+{
+ get_arithmetic_value(j, val);
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
+{
+ get_arithmetic_value(j, val);
+}
+
+template<typename BasicJsonType, typename EnumType,
+ enable_if_t<std::is_enum<EnumType>::value, int> = 0>
+void from_json(const BasicJsonType& j, EnumType& e)
+{
+ typename std::underlying_type<EnumType>::type val;
+ get_arithmetic_value(j, val);
+ e = static_cast<EnumType>(val);
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::array_t& arr)
+{
+ if (JSON_UNLIKELY(not j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
+ }
+ arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
+}
+
+// forward_list doesn't have an insert method
+template<typename BasicJsonType, typename T, typename Allocator,
+ enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>
+void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
+{
+ if (JSON_UNLIKELY(not j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
+ }
+ std::transform(j.rbegin(), j.rend(),
+ std::front_inserter(l), [](const BasicJsonType & i)
+ {
+ return i.template get<T>();
+ });
+}
+
+// valarray doesn't have an insert method
+template<typename BasicJsonType, typename T,
+ enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>
+void from_json(const BasicJsonType& j, std::valarray<T>& l)
+{
+ if (JSON_UNLIKELY(not j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
+ }
+ l.resize(j.size());
+ std::copy(j.m_value.array->begin(), j.m_value.array->end(), std::begin(l));
+}
+
+template<typename BasicJsonType, typename CompatibleArrayType>
+void from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<0> /*unused*/)
+{
+ using std::end;
+
+ std::transform(j.begin(), j.end(),
+ std::inserter(arr, end(arr)), [](const BasicJsonType & i)
+ {
+ // get<BasicJsonType>() returns *this, this won't call a from_json
+ // method when value_type is BasicJsonType
+ return i.template get<typename CompatibleArrayType::value_type>();
+ });
+}
+
+template<typename BasicJsonType, typename CompatibleArrayType>
+auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<1> /*unused*/)
+-> decltype(
+ arr.reserve(std::declval<typename CompatibleArrayType::size_type>()),
+ void())
+{
+ using std::end;
+
+ arr.reserve(j.size());
+ std::transform(j.begin(), j.end(),
+ std::inserter(arr, end(arr)), [](const BasicJsonType & i)
+ {
+ // get<BasicJsonType>() returns *this, this won't call a from_json
+ // method when value_type is BasicJsonType
+ return i.template get<typename CompatibleArrayType::value_type>();
+ });
+}
+
+template<typename BasicJsonType, typename T, std::size_t N>
+void from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr, priority_tag<2> /*unused*/)
+{
+ for (std::size_t i = 0; i < N; ++i)
+ {
+ arr[i] = j.at(i).template get<T>();
+ }
+}
+
+template<typename BasicJsonType, typename CompatibleArrayType,
+ enable_if_t<is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value and
+ std::is_convertible<BasicJsonType, typename CompatibleArrayType::value_type>::value and
+ not std::is_same<typename BasicJsonType::array_t, CompatibleArrayType>::value, int> = 0>
+void from_json(const BasicJsonType& j, CompatibleArrayType& arr)
+{
+ if (JSON_UNLIKELY(not j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
+ }
+
+ from_json_array_impl(j, arr, priority_tag<2> {});
+}
+
+template<typename BasicJsonType, typename CompatibleObjectType,
+ enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value, int> = 0>
+void from_json(const BasicJsonType& j, CompatibleObjectType& obj)
+{
+ if (JSON_UNLIKELY(not j.is_object()))
+ {
+ JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name())));
+ }
+
+ auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
+ using value_type = typename CompatibleObjectType::value_type;
+ std::transform(
+ inner_object->begin(), inner_object->end(),
+ std::inserter(obj, obj.begin()),
+ [](typename BasicJsonType::object_t::value_type const & p)
+ {
+ return value_type(p.first, p.second.template get<typename CompatibleObjectType::mapped_type>());
+ });
+}
+
+// overload for arithmetic types, not chosen for basic_json template arguments
+// (BooleanType, etc..); note: Is it really necessary to provide explicit
+// overloads for boolean_t etc. in case of a custom BooleanType which is not
+// an arithmetic type?
+template<typename BasicJsonType, typename ArithmeticType,
+ enable_if_t <
+ std::is_arithmetic<ArithmeticType>::value and
+ not std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value and
+ not std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value and
+ not std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value and
+ not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
+ int> = 0>
+void from_json(const BasicJsonType& j, ArithmeticType& val)
+{
+ switch (static_cast<value_t>(j))
+ {
+ case value_t::number_unsigned:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
+ break;
+ }
+ case value_t::number_integer:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
+ break;
+ }
+ case value_t::number_float:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
+ break;
+ }
+ case value_t::boolean:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
+ break;
+ }
+
+ default:
+ JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name())));
+ }
+}
+
+template<typename BasicJsonType, typename A1, typename A2>
+void from_json(const BasicJsonType& j, std::pair<A1, A2>& p)
+{
+ p = {j.at(0).template get<A1>(), j.at(1).template get<A2>()};
+}
+
+template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
+void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence<Idx...>)
+{
+ t = std::make_tuple(j.at(Idx).template get<typename std::tuple_element<Idx, Tuple>::type>()...);
+}
+
+template<typename BasicJsonType, typename... Args>
+void from_json(const BasicJsonType& j, std::tuple<Args...>& t)
+{
+ from_json_tuple_impl(j, t, index_sequence_for<Args...> {});
+}
+
+struct to_json_fn
+{
+ private:
+ template<typename BasicJsonType, typename T>
+ auto call(BasicJsonType& j, T&& val, priority_tag<1> /*unused*/) const noexcept(noexcept(to_json(j, std::forward<T>(val))))
+ -> decltype(to_json(j, std::forward<T>(val)), void())
+ {
+ return to_json(j, std::forward<T>(val));
+ }
+
+ template<typename BasicJsonType, typename T>
+ void call(BasicJsonType& /*unused*/, T&& /*unused*/, priority_tag<0> /*unused*/) const noexcept
+ {
+ static_assert(sizeof(BasicJsonType) == 0,
+ "could not find to_json() method in T's namespace");
+
+#ifdef _MSC_VER
+ // MSVC does not show a stacktrace for the above assert
+ using decayed = uncvref_t<T>;
+ static_assert(sizeof(typename decayed::force_msvc_stacktrace) == 0,
+ "forcing MSVC stacktrace to show which T we're talking about.");
+#endif
+ }
+
+ public:
+ template<typename BasicJsonType, typename T>
+ void operator()(BasicJsonType& j, T&& val) const
+ noexcept(noexcept(std::declval<to_json_fn>().call(j, std::forward<T>(val), priority_tag<1> {})))
+ {
+ return call(j, std::forward<T>(val), priority_tag<1> {});
+ }
+};
+
+struct from_json_fn
+{
+ private:
+ template<typename BasicJsonType, typename T>
+ auto call(const BasicJsonType& j, T& val, priority_tag<1> /*unused*/) const
+ noexcept(noexcept(from_json(j, val)))
+ -> decltype(from_json(j, val), void())
+ {
+ return from_json(j, val);
+ }
+
+ template<typename BasicJsonType, typename T>
+ void call(const BasicJsonType& /*unused*/, T& /*unused*/, priority_tag<0> /*unused*/) const noexcept
+ {
+ static_assert(sizeof(BasicJsonType) == 0,
+ "could not find from_json() method in T's namespace");
+#ifdef _MSC_VER
+ // MSVC does not show a stacktrace for the above assert
+ using decayed = uncvref_t<T>;
+ static_assert(sizeof(typename decayed::force_msvc_stacktrace) == 0,
+ "forcing MSVC stacktrace to show which T we're talking about.");
+#endif
+ }
+
+ public:
+ template<typename BasicJsonType, typename T>
+ void operator()(const BasicJsonType& j, T& val) const
+ noexcept(noexcept(std::declval<from_json_fn>().call(j, val, priority_tag<1> {})))
+ {
+ return call(j, val, priority_tag<1> {});
+ }
+};
+
+// taken from ranges-v3
+template<typename T>
+struct static_const
+{
+ static constexpr T value{};
+};
+
+template<typename T>
+constexpr T static_const<T>::value;
+
+////////////////////
+// input adapters //
+////////////////////
+
+/*!
+@brief abstract input adapter interface
+
+Produces a stream of std::char_traits<char>::int_type characters from a
+std::istream, a buffer, or some other input type. Accepts the return of exactly
+one non-EOF character for future input. The int_type characters returned
+consist of all valid char values as positive values (typically unsigned char),
+plus an EOF value outside that range, specified by the value of the function
+std::char_traits<char>::eof(). This value is typically -1, but could be any
+arbitrary value which is not a valid char value.
+*/
+struct input_adapter_protocol
+{
+ /// get a character [0,255] or std::char_traits<char>::eof().
+ virtual std::char_traits<char>::int_type get_character() = 0;
+ /// restore the last non-eof() character to input
+ virtual void unget_character() = 0;
+ virtual ~input_adapter_protocol() = default;
+};
+
+/// a type to simplify interfaces
+using input_adapter_t = std::shared_ptr<input_adapter_protocol>;
+
+/*!
+Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at
+beginning of input. Does not support changing the underlying std::streambuf
+in mid-input. Maintains underlying std::istream and std::streambuf to support
+subsequent use of standard std::istream operations to process any input
+characters following those used in parsing the JSON input. Clears the
+std::istream flags; any input errors (e.g., EOF) will be detected by the first
+subsequent call for input from the std::istream.
+*/
+class input_stream_adapter : public input_adapter_protocol
+{
+ public:
+ ~input_stream_adapter() override
+ {
+ // clear stream flags; we use underlying streambuf I/O, do not
+ // maintain ifstream flags
+ is.clear();
+ }
+
+ explicit input_stream_adapter(std::istream& i)
+ : is(i), sb(*i.rdbuf())
+ {
+ // skip byte order mark
+ std::char_traits<char>::int_type c;
+ if ((c = get_character()) == 0xEF)
+ {
+ if ((c = get_character()) == 0xBB)
+ {
+ if ((c = get_character()) == 0xBF)
+ {
+ return; // Ignore BOM
+ }
+ else if (c != std::char_traits<char>::eof())
+ {
+ is.unget();
+ }
+ is.putback('\xBB');
+ }
+ else if (c != std::char_traits<char>::eof())
+ {
+ is.unget();
+ }
+ is.putback('\xEF');
+ }
+ else if (c != std::char_traits<char>::eof())
+ {
+ is.unget(); // no byte order mark; process as usual
+ }
+ }
+
+ // delete because of pointer members
+ input_stream_adapter(const input_stream_adapter&) = delete;
+ input_stream_adapter& operator=(input_stream_adapter&) = delete;
+
+ // std::istream/std::streambuf use std::char_traits<char>::to_int_type, to
+ // ensure that std::char_traits<char>::eof() and the character 0xFF do not
+ // end up as the same value, eg. 0xFFFFFFFF.
+ std::char_traits<char>::int_type get_character() override
+ {
+ return sb.sbumpc();
+ }
+
+ void unget_character() override
+ {
+ sb.sungetc(); // is.unget() avoided for performance
+ }
+
+ private:
+ /// the associated input stream
+ std::istream& is;
+ std::streambuf& sb;
+};
+
+/// input adapter for buffer input
+class input_buffer_adapter : public input_adapter_protocol
+{
+ public:
+ input_buffer_adapter(const char* b, const std::size_t l)
+ : cursor(b), limit(b + l), start(b)
+ {
+ // skip byte order mark
+ if (l >= 3 and b[0] == '\xEF' and b[1] == '\xBB' and b[2] == '\xBF')
+ {
+ cursor += 3;
+ }
+ }
+
+ // delete because of pointer members
+ input_buffer_adapter(const input_buffer_adapter&) = delete;
+ input_buffer_adapter& operator=(input_buffer_adapter&) = delete;
+
+ std::char_traits<char>::int_type get_character() noexcept override
+ {
+ if (JSON_LIKELY(cursor < limit))
+ {
+ return std::char_traits<char>::to_int_type(*(cursor++));
+ }
+
+ return std::char_traits<char>::eof();
+ }
+
+ void unget_character() noexcept override
+ {
+ if (JSON_LIKELY(cursor > start))
+ {
+ --cursor;
+ }
+ }
+
+ private:
+ /// pointer to the current character
+ const char* cursor;
+ /// pointer past the last character
+ const char* limit;
+ /// pointer to the first character
+ const char* start;
+};
+
+class input_adapter
+{
+ public:
+ // native support
+
+ /// input adapter for input stream
+ input_adapter(std::istream& i)
+ : ia(std::make_shared<input_stream_adapter>(i)) {}
+
+ /// input adapter for input stream
+ input_adapter(std::istream&& i)
+ : ia(std::make_shared<input_stream_adapter>(i)) {}
+
+ /// input adapter for buffer
+ template<typename CharT,
+ typename std::enable_if<
+ std::is_pointer<CharT>::value and
+ std::is_integral<typename std::remove_pointer<CharT>::type>::value and
+ sizeof(typename std::remove_pointer<CharT>::type) == 1,
+ int>::type = 0>
+ input_adapter(CharT b, std::size_t l)
+ : ia(std::make_shared<input_buffer_adapter>(reinterpret_cast<const char*>(b), l)) {}
+
+ // derived support
+
+ /// input adapter for string literal
+ template<typename CharT,
+ typename std::enable_if<
+ std::is_pointer<CharT>::value and
+ std::is_integral<typename std::remove_pointer<CharT>::type>::value and
+ sizeof(typename std::remove_pointer<CharT>::type) == 1,
+ int>::type = 0>
+ input_adapter(CharT b)
+ : input_adapter(reinterpret_cast<const char*>(b),
+ std::strlen(reinterpret_cast<const char*>(b))) {}
+
+ /// input adapter for iterator range with contiguous storage
+ template<class IteratorType,
+ typename std::enable_if<
+ std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,
+ int>::type = 0>
+ input_adapter(IteratorType first, IteratorType last)
+ {
+ // assertion to check that the iterator range is indeed contiguous,
+ // see http://stackoverflow.com/a/35008842/266378 for more discussion
+ assert(std::accumulate(
+ first, last, std::pair<bool, int>(true, 0),
+ [&first](std::pair<bool, int> res, decltype(*first) val)
+ {
+ res.first &= (val == *(std::next(std::addressof(*first), res.second++)));
+ return res;
+ }).first);
+
+ // assertion to check that each element is 1 byte long
+ static_assert(
+ sizeof(typename std::iterator_traits<IteratorType>::value_type) == 1,
+ "each element in the iterator range must have the size of 1 byte");
+
+ const auto len = static_cast<size_t>(std::distance(first, last));
+ if (JSON_LIKELY(len > 0))
+ {
+ // there is at least one element: use the address of first
+ ia = std::make_shared<input_buffer_adapter>(reinterpret_cast<const char*>(&(*first)), len);
+ }
+ else
+ {
+ // the address of first cannot be used: use nullptr
+ ia = std::make_shared<input_buffer_adapter>(nullptr, len);
+ }
+ }
+
+ /// input adapter for array
+ template<class T, std::size_t N>
+ input_adapter(T (&array)[N])
+ : input_adapter(std::begin(array), std::end(array)) {}
+
+ /// input adapter for contiguous container
+ template<class ContiguousContainer, typename
+ std::enable_if<not std::is_pointer<ContiguousContainer>::value and
+ std::is_base_of<std::random_access_iterator_tag, typename std::iterator_traits<decltype(std::begin(std::declval<ContiguousContainer const>()))>::iterator_category>::value,
+ int>::type = 0>
+ input_adapter(const ContiguousContainer& c)
+ : input_adapter(std::begin(c), std::end(c)) {}
+
+ operator input_adapter_t()
+ {
+ return ia;
+ }
+
+ private:
+ /// the actual adapter
+ input_adapter_t ia = nullptr;
+};
+
+//////////////////////
+// lexer and parser //
+//////////////////////
+
+/*!
+@brief lexical analysis
+
+This class organizes the lexical analysis during JSON deserialization.
+*/
+template<typename BasicJsonType>
+class lexer
+{
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+
+ public:
+ /// token types for the parser
+ enum class token_type
+ {
+ uninitialized, ///< indicating the scanner is uninitialized
+ literal_true, ///< the `true` literal
+ literal_false, ///< the `false` literal
+ literal_null, ///< the `null` literal
+ value_string, ///< a string -- use get_string() for actual value
+ value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value
+ value_integer, ///< a signed integer -- use get_number_integer() for actual value
+ value_float, ///< an floating point number -- use get_number_float() for actual value
+ begin_array, ///< the character for array begin `[`
+ begin_object, ///< the character for object begin `{`
+ end_array, ///< the character for array end `]`
+ end_object, ///< the character for object end `}`
+ name_separator, ///< the name separator `:`
+ value_separator, ///< the value separator `,`
+ parse_error, ///< indicating a parse error
+ end_of_input, ///< indicating the end of the input buffer
+ literal_or_value ///< a literal or the begin of a value (only for diagnostics)
+ };
+
+ /// return name of values of type token_type (only used for errors)
+ static const char* token_type_name(const token_type t) noexcept
+ {
+ switch (t)
+ {
+ case token_type::uninitialized:
+ return "<uninitialized>";
+ case token_type::literal_true:
+ return "true literal";
+ case token_type::literal_false:
+ return "false literal";
+ case token_type::literal_null:
+ return "null literal";
+ case token_type::value_string:
+ return "string literal";
+ case lexer::token_type::value_unsigned:
+ case lexer::token_type::value_integer:
+ case lexer::token_type::value_float:
+ return "number literal";
+ case token_type::begin_array:
+ return "'['";
+ case token_type::begin_object:
+ return "'{'";
+ case token_type::end_array:
+ return "']'";
+ case token_type::end_object:
+ return "'}'";
+ case token_type::name_separator:
+ return "':'";
+ case token_type::value_separator:
+ return "','";
+ case token_type::parse_error:
+ return "<parse error>";
+ case token_type::end_of_input:
+ return "end of input";
+ case token_type::literal_or_value:
+ return "'[', '{', or a literal";
+ default: // catch non-enum values
+ return "unknown token"; // LCOV_EXCL_LINE
+ }
+ }
+
+ explicit lexer(detail::input_adapter_t adapter)
+ : ia(std::move(adapter)), decimal_point_char(get_decimal_point()) {}
+
+ // delete because of pointer members
+ lexer(const lexer&) = delete;
+ lexer& operator=(lexer&) = delete;
+
+ private:
+ /////////////////////
+ // locales
+ /////////////////////
+
+ /// return the locale-dependent decimal point
+ static char get_decimal_point() noexcept
+ {
+ const auto loc = localeconv();
+ assert(loc != nullptr);
+ return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);
+ }
+
+ /////////////////////
+ // scan functions
+ /////////////////////
+
+ /*!
+ @brief get codepoint from 4 hex characters following `\u`
+
+ For input "\u c1 c2 c3 c4" the codepoint is:
+ (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4
+ = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0)
+
+ Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f'
+ must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The
+ conversion is done by subtracting the offset (0x30, 0x37, and 0x57)
+ between the ASCII value of the character and the desired integer value.
+
+ @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or
+ non-hex character)
+ */
+ int get_codepoint()
+ {
+ // this function only makes sense after reading `\u`
+ assert(current == 'u');
+ int codepoint = 0;
+
+ const auto factors = { 12, 8, 4, 0 };
+ for (const auto factor : factors)
+ {
+ get();
+
+ if (current >= '0' and current <= '9')
+ {
+ codepoint += ((current - 0x30) << factor);
+ }
+ else if (current >= 'A' and current <= 'F')
+ {
+ codepoint += ((current - 0x37) << factor);
+ }
+ else if (current >= 'a' and current <= 'f')
+ {
+ codepoint += ((current - 0x57) << factor);
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ assert(0x0000 <= codepoint and codepoint <= 0xFFFF);
+ return codepoint;
+ }
+
+ /*!
+ @brief check if the next byte(s) are inside a given range
+
+ Adds the current byte and, for each passed range, reads a new byte and
+ checks if it is inside the range. If a violation was detected, set up an
+ error message and return false. Otherwise, return true.
+
+ @param[in] ranges list of integers; interpreted as list of pairs of
+ inclusive lower and upper bound, respectively
+
+ @pre The passed list @a ranges must have 2, 4, or 6 elements; that is,
+ 1, 2, or 3 pairs. This precondition is enforced by an assertion.
+
+ @return true if and only if no range violation was detected
+ */
+ bool next_byte_in_range(std::initializer_list<int> ranges)
+ {
+ assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6);
+ add(current);
+
+ for (auto range = ranges.begin(); range != ranges.end(); ++range)
+ {
+ get();
+ if (JSON_LIKELY(*range <= current and current <= *(++range)))
+ {
+ add(current);
+ }
+ else
+ {
+ error_message = "invalid string: ill-formed UTF-8 byte";
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /*!
+ @brief scan a string literal
+
+ This function scans a string according to Sect. 7 of RFC 7159. While
+ scanning, bytes are escaped and copied into buffer yytext. Then the function
+ returns successfully, yytext is *not* null-terminated (as it may contain \0
+ bytes), and yytext.size() is the number of bytes in the string.
+
+ @return token_type::value_string if string could be successfully scanned,
+ token_type::parse_error otherwise
+
+ @note In case of errors, variable error_message contains a textual
+ description.
+ */
+ token_type scan_string()
+ {
+ // reset yytext (ignore opening quote)
+ reset();
+
+ // we entered the function by reading an open quote
+ assert(current == '\"');
+
+ while (true)
+ {
+ // get next character
+ switch (get())
+ {
+ // end of file while parsing string
+ case std::char_traits<char>::eof():
+ {
+ error_message = "invalid string: missing closing quote";
+ return token_type::parse_error;
+ }
+
+ // closing quote
+ case '\"':
+ {
+ return token_type::value_string;
+ }
+
+ // escapes
+ case '\\':
+ {
+ switch (get())
+ {
+ // quotation mark
+ case '\"':
+ add('\"');
+ break;
+ // reverse solidus
+ case '\\':
+ add('\\');
+ break;
+ // solidus
+ case '/':
+ add('/');
+ break;
+ // backspace
+ case 'b':
+ add('\b');
+ break;
+ // form feed
+ case 'f':
+ add('\f');
+ break;
+ // line feed
+ case 'n':
+ add('\n');
+ break;
+ // carriage return
+ case 'r':
+ add('\r');
+ break;
+ // tab
+ case 't':
+ add('\t');
+ break;
+
+ // unicode escapes
+ case 'u':
+ {
+ const int codepoint1 = get_codepoint();
+ int codepoint = codepoint1; // start with codepoint1
+
+ if (JSON_UNLIKELY(codepoint1 == -1))
+ {
+ error_message = "invalid string: '\\u' must be followed by 4 hex digits";
+ return token_type::parse_error;
+ }
+
+ // check if code point is a high surrogate
+ if (0xD800 <= codepoint1 and codepoint1 <= 0xDBFF)
+ {
+ // expect next \uxxxx entry
+ if (JSON_LIKELY(get() == '\\' and get() == 'u'))
+ {
+ const int codepoint2 = get_codepoint();
+
+ if (JSON_UNLIKELY(codepoint2 == -1))
+ {
+ error_message = "invalid string: '\\u' must be followed by 4 hex digits";
+ return token_type::parse_error;
+ }
+
+ // check if codepoint2 is a low surrogate
+ if (JSON_LIKELY(0xDC00 <= codepoint2 and codepoint2 <= 0xDFFF))
+ {
+ // overwrite codepoint
+ codepoint =
+ // high surrogate occupies the most significant 22 bits
+ (codepoint1 << 10)
+ // low surrogate occupies the least significant 15 bits
+ + codepoint2
+ // there is still the 0xD800, 0xDC00 and 0x10000 noise
+ // in the result so we have to subtract with:
+ // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00
+ - 0x35FDC00;
+ }
+ else
+ {
+ error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF";
+ return token_type::parse_error;
+ }
+ }
+ else
+ {
+ error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF";
+ return token_type::parse_error;
+ }
+ }
+ else
+ {
+ if (JSON_UNLIKELY(0xDC00 <= codepoint1 and codepoint1 <= 0xDFFF))
+ {
+ error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF";
+ return token_type::parse_error;
+ }
+ }
+
+ // result of the above calculation yields a proper codepoint
+ assert(0x00 <= codepoint and codepoint <= 0x10FFFF);
+
+ // translate codepoint into bytes
+ if (codepoint < 0x80)
+ {
+ // 1-byte characters: 0xxxxxxx (ASCII)
+ add(codepoint);
+ }
+ else if (codepoint <= 0x7FF)
+ {
+ // 2-byte characters: 110xxxxx 10xxxxxx
+ add(0xC0 | (codepoint >> 6));
+ add(0x80 | (codepoint & 0x3F));
+ }
+ else if (codepoint <= 0xFFFF)
+ {
+ // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx
+ add(0xE0 | (codepoint >> 12));
+ add(0x80 | ((codepoint >> 6) & 0x3F));
+ add(0x80 | (codepoint & 0x3F));
+ }
+ else
+ {
+ // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ add(0xF0 | (codepoint >> 18));
+ add(0x80 | ((codepoint >> 12) & 0x3F));
+ add(0x80 | ((codepoint >> 6) & 0x3F));
+ add(0x80 | (codepoint & 0x3F));
+ }
+
+ break;
+ }
+
+ // other characters after escape
+ default:
+ error_message = "invalid string: forbidden character after backslash";
+ return token_type::parse_error;
+ }
+
+ break;
+ }
+
+ // invalid control characters
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0A:
+ case 0x0B:
+ case 0x0C:
+ case 0x0D:
+ case 0x0E:
+ case 0x0F:
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ case 0x18:
+ case 0x19:
+ case 0x1A:
+ case 0x1B:
+ case 0x1C:
+ case 0x1D:
+ case 0x1E:
+ case 0x1F:
+ {
+ error_message = "invalid string: control character must be escaped";
+ return token_type::parse_error;
+ }
+
+ // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace))
+ case 0x20:
+ case 0x21:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ case 0x2C:
+ case 0x2D:
+ case 0x2E:
+ case 0x2F:
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ case 0x3A:
+ case 0x3B:
+ case 0x3C:
+ case 0x3D:
+ case 0x3E:
+ case 0x3F:
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4A:
+ case 0x4B:
+ case 0x4C:
+ case 0x4D:
+ case 0x4E:
+ case 0x4F:
+ case 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ case 0x54:
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ case 0x58:
+ case 0x59:
+ case 0x5A:
+ case 0x5B:
+ case 0x5D:
+ case 0x5E:
+ case 0x5F:
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ case 0x78:
+ case 0x79:
+ case 0x7A:
+ case 0x7B:
+ case 0x7C:
+ case 0x7D:
+ case 0x7E:
+ case 0x7F:
+ {
+ add(current);
+ break;
+ }
+
+ // U+0080..U+07FF: bytes C2..DF 80..BF
+ case 0xC2:
+ case 0xC3:
+ case 0xC4:
+ case 0xC5:
+ case 0xC6:
+ case 0xC7:
+ case 0xC8:
+ case 0xC9:
+ case 0xCA:
+ case 0xCB:
+ case 0xCC:
+ case 0xCD:
+ case 0xCE:
+ case 0xCF:
+ case 0xD0:
+ case 0xD1:
+ case 0xD2:
+ case 0xD3:
+ case 0xD4:
+ case 0xD5:
+ case 0xD6:
+ case 0xD7:
+ case 0xD8:
+ case 0xD9:
+ case 0xDA:
+ case 0xDB:
+ case 0xDC:
+ case 0xDD:
+ case 0xDE:
+ case 0xDF:
+ {
+ if (JSON_UNLIKELY(not next_byte_in_range({0x80, 0xBF})))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+0800..U+0FFF: bytes E0 A0..BF 80..BF
+ case 0xE0:
+ {
+ if (JSON_UNLIKELY(not (next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF
+ // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF
+ case 0xE1:
+ case 0xE2:
+ case 0xE3:
+ case 0xE4:
+ case 0xE5:
+ case 0xE6:
+ case 0xE7:
+ case 0xE8:
+ case 0xE9:
+ case 0xEA:
+ case 0xEB:
+ case 0xEC:
+ case 0xEE:
+ case 0xEF:
+ {
+ if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+D000..U+D7FF: bytes ED 80..9F 80..BF
+ case 0xED:
+ {
+ if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0x9F, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF
+ case 0xF0:
+ {
+ if (JSON_UNLIKELY(not (next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF
+ case 0xF1:
+ case 0xF2:
+ case 0xF3:
+ {
+ if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF
+ case 0xF4:
+ {
+ if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // remaining bytes (80..C1 and F5..FF) are ill-formed
+ default:
+ {
+ error_message = "invalid string: ill-formed UTF-8 byte";
+ return token_type::parse_error;
+ }
+ }
+ }
+ }
+
+ static void strtof(float& f, const char* str, char** endptr) noexcept
+ {
+ f = std::strtof(str, endptr);
+ }
+
+ static void strtof(double& f, const char* str, char** endptr) noexcept
+ {
+ f = std::strtod(str, endptr);
+ }
+
+ static void strtof(long double& f, const char* str, char** endptr) noexcept
+ {
+ f = std::strtold(str, endptr);
+ }
+
+ /*!
+ @brief scan a number literal
+
+ This function scans a string according to Sect. 6 of RFC 7159.
+
+ The function is realized with a deterministic finite state machine derived
+ from the grammar described in RFC 7159. Starting in state "init", the
+ input is read and used to determined the next state. Only state "done"
+ accepts the number. State "error" is a trap state to model errors. In the
+ table below, "anything" means any character but the ones listed before.
+
+ state | 0 | 1-9 | e E | + | - | . | anything
+ ---------|----------|----------|----------|---------|---------|----------|-----------
+ init | zero | any1 | [error] | [error] | minus | [error] | [error]
+ minus | zero | any1 | [error] | [error] | [error] | [error] | [error]
+ zero | done | done | exponent | done | done | decimal1 | done
+ any1 | any1 | any1 | exponent | done | done | decimal1 | done
+ decimal1 | decimal2 | [error] | [error] | [error] | [error] | [error] | [error]
+ decimal2 | decimal2 | decimal2 | exponent | done | done | done | done
+ exponent | any2 | any2 | [error] | sign | sign | [error] | [error]
+ sign | any2 | any2 | [error] | [error] | [error] | [error] | [error]
+ any2 | any2 | any2 | done | done | done | done | done
+
+ The state machine is realized with one label per state (prefixed with
+ "scan_number_") and `goto` statements between them. The state machine
+ contains cycles, but any cycle can be left when EOF is read. Therefore,
+ the function is guaranteed to terminate.
+
+ During scanning, the read bytes are stored in yytext. This string is
+ then converted to a signed integer, an unsigned integer, or a
+ floating-point number.
+
+ @return token_type::value_unsigned, token_type::value_integer, or
+ token_type::value_float if number could be successfully scanned,
+ token_type::parse_error otherwise
+
+ @note The scanner is independent of the current locale. Internally, the
+ locale's decimal point is used instead of `.` to work with the
+ locale-dependent converters.
+ */
+ token_type scan_number()
+ {
+ // reset yytext to store the number's bytes
+ reset();
+
+ // the type of the parsed number; initially set to unsigned; will be
+ // changed if minus sign, decimal point or exponent is read
+ token_type number_type = token_type::value_unsigned;
+
+ // state (init): we just found out we need to scan a number
+ switch (current)
+ {
+ case '-':
+ {
+ add(current);
+ goto scan_number_minus;
+ }
+
+ case '0':
+ {
+ add(current);
+ goto scan_number_zero;
+ }
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any1;
+ }
+
+ default:
+ {
+ // all other characters are rejected outside scan_number()
+ assert(false); // LCOV_EXCL_LINE
+ }
+ }
+
+scan_number_minus:
+ // state: we just parsed a leading minus sign
+ number_type = token_type::value_integer;
+ switch (get())
+ {
+ case '0':
+ {
+ add(current);
+ goto scan_number_zero;
+ }
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any1;
+ }
+
+ default:
+ {
+ error_message = "invalid number; expected digit after '-'";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_zero:
+ // state: we just parse a zero (maybe with a leading minus sign)
+ switch (get())
+ {
+ case '.':
+ {
+ add(decimal_point_char);
+ goto scan_number_decimal1;
+ }
+
+ case 'e':
+ case 'E':
+ {
+ add(current);
+ goto scan_number_exponent;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_any1:
+ // state: we just parsed a number 0-9 (maybe with a leading minus sign)
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any1;
+ }
+
+ case '.':
+ {
+ add(decimal_point_char);
+ goto scan_number_decimal1;
+ }
+
+ case 'e':
+ case 'E':
+ {
+ add(current);
+ goto scan_number_exponent;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_decimal1:
+ // state: we just parsed a decimal point
+ number_type = token_type::value_float;
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_decimal2;
+ }
+
+ default:
+ {
+ error_message = "invalid number; expected digit after '.'";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_decimal2:
+ // we just parsed at least one number after a decimal point
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_decimal2;
+ }
+
+ case 'e':
+ case 'E':
+ {
+ add(current);
+ goto scan_number_exponent;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_exponent:
+ // we just parsed an exponent
+ number_type = token_type::value_float;
+ switch (get())
+ {
+ case '+':
+ case '-':
+ {
+ add(current);
+ goto scan_number_sign;
+ }
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any2;
+ }
+
+ default:
+ {
+ error_message =
+ "invalid number; expected '+', '-', or digit after exponent";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_sign:
+ // we just parsed an exponent sign
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any2;
+ }
+
+ default:
+ {
+ error_message = "invalid number; expected digit after exponent sign";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_any2:
+ // we just parsed a number after the exponent or exponent sign
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any2;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_done:
+ // unget the character after the number (we only read it to know that
+ // we are done scanning a number)
+ unget();
+
+ char* endptr = nullptr;
+ errno = 0;
+
+ // try to parse integers first and fall back to floats
+ if (number_type == token_type::value_unsigned)
+ {
+ const auto x = std::strtoull(yytext.data(), &endptr, 10);
+
+ // we checked the number format before
+ assert(endptr == yytext.data() + yytext.size());
+
+ if (errno == 0)
+ {
+ value_unsigned = static_cast<number_unsigned_t>(x);
+ if (value_unsigned == x)
+ {
+ return token_type::value_unsigned;
+ }
+ }
+ }
+ else if (number_type == token_type::value_integer)
+ {
+ const auto x = std::strtoll(yytext.data(), &endptr, 10);
+
+ // we checked the number format before
+ assert(endptr == yytext.data() + yytext.size());
+
+ if (errno == 0)
+ {
+ value_integer = static_cast<number_integer_t>(x);
+ if (value_integer == x)
+ {
+ return token_type::value_integer;
+ }
+ }
+ }
+
+ // this code is reached if we parse a floating-point number or if an
+ // integer conversion above failed
+ strtof(value_float, yytext.data(), &endptr);
+
+ // we checked the number format before
+ assert(endptr == yytext.data() + yytext.size());
+
+ return token_type::value_float;
+ }
+
+ /*!
+ @param[in] literal_text the literal text to expect
+ @param[in] length the length of the passed literal text
+ @param[in] return_type the token type to return on success
+ */
+ token_type scan_literal(const char* literal_text, const std::size_t length,
+ token_type return_type)
+ {
+ assert(current == literal_text[0]);
+ for (std::size_t i = 1; i < length; ++i)
+ {
+ if (JSON_UNLIKELY(get() != literal_text[i]))
+ {
+ error_message = "invalid literal";
+ return token_type::parse_error;
+ }
+ }
+ return return_type;
+ }
+
+ /////////////////////
+ // input management
+ /////////////////////
+
+ /// reset yytext; current character is beginning of token
+ void reset() noexcept
+ {
+ yytext.clear();
+ token_string.clear();
+ token_string.push_back(std::char_traits<char>::to_char_type(current));
+ }
+
+ /*
+ @brief get next character from the input
+
+ This function provides the interface to the used input adapter. It does
+ not throw in case the input reached EOF, but returns a
+ `std::char_traits<char>::eof()` in that case. Stores the scanned characters
+ for use in error messages.
+
+ @return character read from the input
+ */
+ std::char_traits<char>::int_type get()
+ {
+ ++chars_read;
+ current = ia->get_character();
+ if (JSON_LIKELY(current != std::char_traits<char>::eof()))
+ {
+ token_string.push_back(std::char_traits<char>::to_char_type(current));
+ }
+ return current;
+ }
+
+ /// unget current character (return it again on next get)
+ void unget()
+ {
+ --chars_read;
+ if (JSON_LIKELY(current != std::char_traits<char>::eof()))
+ {
+ ia->unget_character();
+ assert(token_string.size() != 0);
+ token_string.pop_back();
+ }
+ }
+
+ /// add a character to yytext
+ void add(int c)
+ {
+ yytext.push_back(std::char_traits<char>::to_char_type(c));
+ }
+
+ public:
+ /////////////////////
+ // value getters
+ /////////////////////
+
+ /// return integer value
+ constexpr number_integer_t get_number_integer() const noexcept
+ {
+ return value_integer;
+ }
+
+ /// return unsigned integer value
+ constexpr number_unsigned_t get_number_unsigned() const noexcept
+ {
+ return value_unsigned;
+ }
+
+ /// return floating-point value
+ constexpr number_float_t get_number_float() const noexcept
+ {
+ return value_float;
+ }
+
+ /// return current string value (implicitly resets the token; useful only once)
+ std::string move_string()
+ {
+ return std::move(yytext);
+ }
+
+ /////////////////////
+ // diagnostics
+ /////////////////////
+
+ /// return position of last read token
+ constexpr std::size_t get_position() const noexcept
+ {
+ return chars_read;
+ }
+
+ /// return the last read token (for errors only). Will never contain EOF
+ /// (an arbitrary value that is not a valid char value, often -1), because
+ /// 255 may legitimately occur. May contain NUL, which should be escaped.
+ std::string get_token_string() const
+ {
+ // escape control characters
+ std::string result;
+ for (const auto c : token_string)
+ {
+ if ('\x00' <= c and c <= '\x1F')
+ {
+ // escape control characters
+ std::stringstream ss;
+ ss << "<U+" << std::setw(4) << std::uppercase << std::setfill('0')
+ << std::hex << static_cast<int>(c) << ">";
+ result += ss.str();
+ }
+ else
+ {
+ // add character as is
+ result.push_back(c);
+ }
+ }
+
+ return result;
+ }
+
+ /// return syntax error message
+ constexpr const char* get_error_message() const noexcept
+ {
+ return error_message;
+ }
+
+ /////////////////////
+ // actual scanner
+ /////////////////////
+
+ token_type scan()
+ {
+ // read next character and ignore whitespace
+ do
+ {
+ get();
+ }
+ while (current == ' ' or current == '\t' or current == '\n' or current == '\r');
+
+ switch (current)
+ {
+ // structural characters
+ case '[':
+ return token_type::begin_array;
+ case ']':
+ return token_type::end_array;
+ case '{':
+ return token_type::begin_object;
+ case '}':
+ return token_type::end_object;
+ case ':':
+ return token_type::name_separator;
+ case ',':
+ return token_type::value_separator;
+
+ // literals
+ case 't':
+ return scan_literal("true", 4, token_type::literal_true);
+ case 'f':
+ return scan_literal("false", 5, token_type::literal_false);
+ case 'n':
+ return scan_literal("null", 4, token_type::literal_null);
+
+ // string
+ case '\"':
+ return scan_string();
+
+ // number
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return scan_number();
+
+ // end of input (the null byte is needed when parsing from
+ // string literals)
+ case '\0':
+ case std::char_traits<char>::eof():
+ return token_type::end_of_input;
+
+ // error
+ default:
+ error_message = "invalid literal";
+ return token_type::parse_error;
+ }
+ }
+
+ private:
+ /// input adapter
+ detail::input_adapter_t ia = nullptr;
+
+ /// the current character
+ std::char_traits<char>::int_type current = std::char_traits<char>::eof();
+
+ /// the number of characters read
+ std::size_t chars_read = 0;
+
+ /// raw input token string (for error messages)
+ std::vector<char> token_string {};
+
+ /// buffer for variable-length tokens (numbers, strings)
+ std::string yytext {};
+
+ /// a description of occurred lexer errors
+ const char* error_message = "";
+
+ // number values
+ number_integer_t value_integer = 0;
+ number_unsigned_t value_unsigned = 0;
+ number_float_t value_float = 0;
+
+ /// the decimal point
+ const char decimal_point_char = '.';
+};
+
+/*!
+@brief syntax analysis
+
+This class implements a recursive decent parser.
+*/
+template<typename BasicJsonType>
+class parser
+{
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using lexer_t = lexer<BasicJsonType>;
+ using token_type = typename lexer_t::token_type;
+
+ public:
+ enum class parse_event_t : uint8_t
+ {
+ /// the parser read `{` and started to process a JSON object
+ object_start,
+ /// the parser read `}` and finished processing a JSON object
+ object_end,
+ /// the parser read `[` and started to process a JSON array
+ array_start,
+ /// the parser read `]` and finished processing a JSON array
+ array_end,
+ /// the parser read a key of a value in an object
+ key,
+ /// the parser finished reading a JSON value
+ value
+ };
+
+ using parser_callback_t =
+ std::function<bool(int depth, parse_event_t event, BasicJsonType& parsed)>;
+
+ /// a parser reading from an input adapter
+ explicit parser(detail::input_adapter_t adapter,
+ const parser_callback_t cb = nullptr,
+ const bool allow_exceptions_ = true)
+ : callback(cb), m_lexer(adapter), allow_exceptions(allow_exceptions_)
+ {}
+
+ /*!
+ @brief public parser interface
+
+ @param[in] strict whether to expect the last token to be EOF
+ @param[in,out] result parsed JSON value
+
+ @throw parse_error.101 in case of an unexpected token
+ @throw parse_error.102 if to_unicode fails or surrogate error
+ @throw parse_error.103 if to_unicode fails
+ */
+ void parse(const bool strict, BasicJsonType& result)
+ {
+ // read first token
+ get_token();
+
+ parse_internal(true, result);
+ result.assert_invariant();
+
+ // in strict mode, input must be completely read
+ if (strict)
+ {
+ get_token();
+ expect(token_type::end_of_input);
+ }
+
+ // in case of an error, return discarded value
+ if (errored)
+ {
+ result = value_t::discarded;
+ return;
+ }
+
+ // set top-level value to null if it was discarded by the callback
+ // function
+ if (result.is_discarded())
+ {
+ result = nullptr;
+ }
+ }
+
+ /*!
+ @brief public accept interface
+
+ @param[in] strict whether to expect the last token to be EOF
+ @return whether the input is a proper JSON text
+ */
+ bool accept(const bool strict = true)
+ {
+ // read first token
+ get_token();
+
+ if (not accept_internal())
+ {
+ return false;
+ }
+
+ // strict => last token must be EOF
+ return not strict or (get_token() == token_type::end_of_input);
+ }
+
+ private:
+ /*!
+ @brief the actual parser
+ @throw parse_error.101 in case of an unexpected token
+ @throw parse_error.102 if to_unicode fails or surrogate error
+ @throw parse_error.103 if to_unicode fails
+ */
+ void parse_internal(bool keep, BasicJsonType& result)
+ {
+ // never parse after a parse error was detected
+ assert(not errored);
+
+ // start with a discarded value
+ if (not result.is_discarded())
+ {
+ result.m_value.destroy(result.m_type);
+ result.m_type = value_t::discarded;
+ }
+
+ switch (last_token)
+ {
+ case token_type::begin_object:
+ {
+ if (keep)
+ {
+ if (callback)
+ {
+ keep = callback(depth++, parse_event_t::object_start, result);
+ }
+
+ if (not callback or keep)
+ {
+ // explicitly set result to object to cope with {}
+ result.m_type = value_t::object;
+ result.m_value = value_t::object;
+ }
+ }
+
+ // read next token
+ get_token();
+
+ // closing } -> we are done
+ if (last_token == token_type::end_object)
+ {
+ if (keep and callback and not callback(--depth, parse_event_t::object_end, result))
+ {
+ result.m_value.destroy(result.m_type);
+ result.m_type = value_t::discarded;
+ }
+ break;
+ }
+
+ // parse values
+ std::string key;
+ BasicJsonType value;
+ while (true)
+ {
+ // store key
+ if (not expect(token_type::value_string))
+ {
+ return;
+ }
+ key = m_lexer.move_string();
+
+ bool keep_tag = false;
+ if (keep)
+ {
+ if (callback)
+ {
+ BasicJsonType k(key);
+ keep_tag = callback(depth, parse_event_t::key, k);
+ }
+ else
+ {
+ keep_tag = true;
+ }
+ }
+
+ // parse separator (:)
+ get_token();
+ if (not expect(token_type::name_separator))
+ {
+ return;
+ }
+
+ // parse and add value
+ get_token();
+ value.m_value.destroy(value.m_type);
+ value.m_type = value_t::discarded;
+ parse_internal(keep, value);
+
+ if (JSON_UNLIKELY(errored))
+ {
+ return;
+ }
+
+ if (keep and keep_tag and not value.is_discarded())
+ {
+ result.m_value.object->emplace(std::move(key), std::move(value));
+ }
+
+ // comma -> next value
+ get_token();
+ if (last_token == token_type::value_separator)
+ {
+ get_token();
+ continue;
+ }
+
+ // closing }
+ if (not expect(token_type::end_object))
+ {
+ return;
+ }
+ break;
+ }
+
+ if (keep and callback and not callback(--depth, parse_event_t::object_end, result))
+ {
+ result.m_value.destroy(result.m_type);
+ result.m_type = value_t::discarded;
+ }
+ break;
+ }
+
+ case token_type::begin_array:
+ {
+ if (keep)
+ {
+ if (callback)
+ {
+ keep = callback(depth++, parse_event_t::array_start, result);
+ }
+
+ if (not callback or keep)
+ {
+ // explicitly set result to array to cope with []
+ result.m_type = value_t::array;
+ result.m_value = value_t::array;
+ }
+ }
+
+ // read next token
+ get_token();
+
+ // closing ] -> we are done
+ if (last_token == token_type::end_array)
+ {
+ if (callback and not callback(--depth, parse_event_t::array_end, result))
+ {
+ result.m_value.destroy(result.m_type);
+ result.m_type = value_t::discarded;
+ }
+ break;
+ }
+
+ // parse values
+ BasicJsonType value;
+ while (true)
+ {
+ // parse value
+ value.m_value.destroy(value.m_type);
+ value.m_type = value_t::discarded;
+ parse_internal(keep, value);
+
+ if (JSON_UNLIKELY(errored))
+ {
+ return;
+ }
+
+ if (keep and not value.is_discarded())
+ {
+ result.m_value.array->push_back(std::move(value));
+ }
+
+ // comma -> next value
+ get_token();
+ if (last_token == token_type::value_separator)
+ {
+ get_token();
+ continue;
+ }
+
+ // closing ]
+ if (not expect(token_type::end_array))
+ {
+ return;
+ }
+ break;
+ }
+
+ if (keep and callback and not callback(--depth, parse_event_t::array_end, result))
+ {
+ result.m_value.destroy(result.m_type);
+ result.m_type = value_t::discarded;
+ }
+ break;
+ }
+
+ case token_type::literal_null:
+ {
+ result.m_type = value_t::null;
+ break;
+ }
+
+ case token_type::value_string:
+ {
+ result.m_type = value_t::string;
+ result.m_value = m_lexer.move_string();
+ break;
+ }
+
+ case token_type::literal_true:
+ {
+ result.m_type = value_t::boolean;
+ result.m_value = true;
+ break;
+ }
+
+ case token_type::literal_false:
+ {
+ result.m_type = value_t::boolean;
+ result.m_value = false;
+ break;
+ }
+
+ case token_type::value_unsigned:
+ {
+ result.m_type = value_t::number_unsigned;
+ result.m_value = m_lexer.get_number_unsigned();
+ break;
+ }
+
+ case token_type::value_integer:
+ {
+ result.m_type = value_t::number_integer;
+ result.m_value = m_lexer.get_number_integer();
+ break;
+ }
+
+ case token_type::value_float:
+ {
+ result.m_type = value_t::number_float;
+ result.m_value = m_lexer.get_number_float();
+
+ // throw in case of infinity or NAN
+ if (JSON_UNLIKELY(not std::isfinite(result.m_value.number_float)))
+ {
+ if (allow_exceptions)
+ {
+ JSON_THROW(out_of_range::create(406, "number overflow parsing '" +
+ m_lexer.get_token_string() + "'"));
+ }
+ expect(token_type::uninitialized);
+ }
+ break;
+ }
+
+ case token_type::parse_error:
+ {
+ // using "uninitialized" to avoid "expected" message
+ if (not expect(token_type::uninitialized))
+ {
+ return;
+ }
+ break; // LCOV_EXCL_LINE
+ }
+
+ default:
+ {
+ // the last token was unexpected; we expected a value
+ if (not expect(token_type::literal_or_value))
+ {
+ return;
+ }
+ break; // LCOV_EXCL_LINE
+ }
+ }
+
+ if (keep and callback and not callback(depth, parse_event_t::value, result))
+ {
+ result.m_type = value_t::discarded;
+ }
+ }
+
+ /*!
+ @brief the actual acceptor
+
+ @invariant 1. The last token is not yet processed. Therefore, the caller
+ of this function must make sure a token has been read.
+ 2. When this function returns, the last token is processed.
+ That is, the last read character was already considered.
+
+ This invariant makes sure that no token needs to be "unput".
+ */
+ bool accept_internal()
+ {
+ switch (last_token)
+ {
+ case token_type::begin_object:
+ {
+ // read next token
+ get_token();
+
+ // closing } -> we are done
+ if (last_token == token_type::end_object)
+ {
+ return true;
+ }
+
+ // parse values
+ while (true)
+ {
+ // parse key
+ if (last_token != token_type::value_string)
+ {
+ return false;
+ }
+
+ // parse separator (:)
+ get_token();
+ if (last_token != token_type::name_separator)
+ {
+ return false;
+ }
+
+ // parse value
+ get_token();
+ if (not accept_internal())
+ {
+ return false;
+ }
+
+ // comma -> next value
+ get_token();
+ if (last_token == token_type::value_separator)
+ {
+ get_token();
+ continue;
+ }
+
+ // closing }
+ return (last_token == token_type::end_object);
+ }
+ }
+
+ case token_type::begin_array:
+ {
+ // read next token
+ get_token();
+
+ // closing ] -> we are done
+ if (last_token == token_type::end_array)
+ {
+ return true;
+ }
+
+ // parse values
+ while (true)
+ {
+ // parse value
+ if (not accept_internal())
+ {
+ return false;
+ }
+
+ // comma -> next value
+ get_token();
+ if (last_token == token_type::value_separator)
+ {
+ get_token();
+ continue;
+ }
+
+ // closing ]
+ return (last_token == token_type::end_array);
+ }
+ }
+
+ case token_type::value_float:
+ {
+ // reject infinity or NAN
+ return std::isfinite(m_lexer.get_number_float());
+ }
+
+ case token_type::literal_false:
+ case token_type::literal_null:
+ case token_type::literal_true:
+ case token_type::value_integer:
+ case token_type::value_string:
+ case token_type::value_unsigned:
+ return true;
+
+ default: // the last token was unexpected
+ return false;
+ }
+ }
+
+ /// get next token from lexer
+ token_type get_token()
+ {
+ return (last_token = m_lexer.scan());
+ }
+
+ /*!
+ @throw parse_error.101 if expected token did not occur
+ */
+ bool expect(token_type t)
+ {
+ if (JSON_UNLIKELY(t != last_token))
+ {
+ errored = true;
+ expected = t;
+ if (allow_exceptions)
+ {
+ throw_exception();
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ [[noreturn]] void throw_exception() const
+ {
+ std::string error_msg = "syntax error - ";
+ if (last_token == token_type::parse_error)
+ {
+ error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" +
+ m_lexer.get_token_string() + "'";
+ }
+ else
+ {
+ error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token));
+ }
+
+ if (expected != token_type::uninitialized)
+ {
+ error_msg += "; expected " + std::string(lexer_t::token_type_name(expected));
+ }
+
+ JSON_THROW(parse_error::create(101, m_lexer.get_position(), error_msg));
+ }
+
+ private:
+ /// current level of recursion
+ int depth = 0;
+ /// callback function
+ const parser_callback_t callback = nullptr;
+ /// the type of the last read token
+ token_type last_token = token_type::uninitialized;
+ /// the lexer
+ lexer_t m_lexer;
+ /// whether a syntax error occurred
+ bool errored = false;
+ /// possible reason for the syntax error
+ token_type expected = token_type::uninitialized;
+ /// whether to throw exceptions in case of errors
+ const bool allow_exceptions = true;
+};
+
+///////////////
+// iterators //
+///////////////
+
+/*!
+@brief an iterator for primitive JSON types
+
+This class models an iterator for primitive JSON types (boolean, number,
+string). It's only purpose is to allow the iterator/const_iterator classes
+to "iterate" over primitive values. Internally, the iterator is modeled by
+a `difference_type` variable. Value begin_value (`0`) models the begin,
+end_value (`1`) models past the end.
+*/
+class primitive_iterator_t
+{
+ public:
+ using difference_type = std::ptrdiff_t;
+
+ constexpr difference_type get_value() const noexcept
+ {
+ return m_it;
+ }
+
+ /// set iterator to a defined beginning
+ void set_begin() noexcept
+ {
+ m_it = begin_value;
+ }
+
+ /// set iterator to a defined past the end
+ void set_end() noexcept
+ {
+ m_it = end_value;
+ }
+
+ /// return whether the iterator can be dereferenced
+ constexpr bool is_begin() const noexcept
+ {
+ return m_it == begin_value;
+ }
+
+ /// return whether the iterator is at end
+ constexpr bool is_end() const noexcept
+ {
+ return m_it == end_value;
+ }
+
+ friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+ {
+ return lhs.m_it == rhs.m_it;
+ }
+
+ friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+ {
+ return lhs.m_it < rhs.m_it;
+ }
+
+ primitive_iterator_t operator+(difference_type i)
+ {
+ auto result = *this;
+ result += i;
+ return result;
+ }
+
+ friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+ {
+ return lhs.m_it - rhs.m_it;
+ }
+
+ friend std::ostream& operator<<(std::ostream& os, primitive_iterator_t it)
+ {
+ return os << it.m_it;
+ }
+
+ primitive_iterator_t& operator++()
+ {
+ ++m_it;
+ return *this;
+ }
+
+ primitive_iterator_t const operator++(int)
+ {
+ auto result = *this;
+ m_it++;
+ return result;
+ }
+
+ primitive_iterator_t& operator--()
+ {
+ --m_it;
+ return *this;
+ }
+
+ primitive_iterator_t const operator--(int)
+ {
+ auto result = *this;
+ m_it--;
+ return result;
+ }
+
+ primitive_iterator_t& operator+=(difference_type n)
+ {
+ m_it += n;
+ return *this;
+ }
+
+ primitive_iterator_t& operator-=(difference_type n)
+ {
+ m_it -= n;
+ return *this;
+ }
+
+ private:
+ static constexpr difference_type begin_value = 0;
+ static constexpr difference_type end_value = begin_value + 1;
+
+ /// iterator as signed integer type
+ difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();
+};
+
+/*!
+@brief an iterator value
+
+@note This structure could easily be a union, but MSVC currently does not allow
+unions members with complex constructors, see https://github.com/nlohmann/json/pull/105.
+*/
+template<typename BasicJsonType> struct internal_iterator
+{
+ /// iterator for JSON objects
+ typename BasicJsonType::object_t::iterator object_iterator {};
+ /// iterator for JSON arrays
+ typename BasicJsonType::array_t::iterator array_iterator {};
+ /// generic iterator for all other types
+ primitive_iterator_t primitive_iterator {};
+};
+
+template<typename IteratorType> class iteration_proxy;
+
+/*!
+@brief a template for a bidirectional iterator for the @ref basic_json class
+
+This class implements a both iterators (iterator and const_iterator) for the
+@ref basic_json class.
+
+@note An iterator is called *initialized* when a pointer to a JSON value has
+ been set (e.g., by a constructor or a copy assignment). If the iterator is
+ default-constructed, it is *uninitialized* and most methods are undefined.
+ **The library uses assertions to detect calls on uninitialized iterators.**
+
+@requirement The class satisfies the following concept requirements:
+-
+[BidirectionalIterator](http://en.cppreference.com/w/cpp/concept/BidirectionalIterator):
+ The iterator that can be moved can be moved in both directions (i.e.
+ incremented and decremented).
+
+@since version 1.0.0, simplified in version 2.0.9, change to bidirectional
+ iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)
+*/
+template<typename BasicJsonType>
+class iter_impl
+{
+ /// allow basic_json to access private members
+ friend iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;
+ friend BasicJsonType;
+ friend iteration_proxy<iter_impl>;
+
+ using object_t = typename BasicJsonType::object_t;
+ using array_t = typename BasicJsonType::array_t;
+ // make sure BasicJsonType is basic_json or const basic_json
+ static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
+ "iter_impl only accepts (const) basic_json");
+
+ public:
+
+ /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.
+ /// The C++ Standard has never required user-defined iterators to derive from std::iterator.
+ /// A user-defined iterator should provide publicly accessible typedefs named
+ /// iterator_category, value_type, difference_type, pointer, and reference.
+ /// Note that value_type is required to be non-const, even for constant iterators.
+ using iterator_category = std::bidirectional_iterator_tag;
+
+ /// the type of the values when the iterator is dereferenced
+ using value_type = typename BasicJsonType::value_type;
+ /// a type to represent differences between iterators
+ using difference_type = typename BasicJsonType::difference_type;
+ /// defines a pointer to the type iterated over (value_type)
+ using pointer = typename std::conditional<std::is_const<BasicJsonType>::value,
+ typename BasicJsonType::const_pointer,
+ typename BasicJsonType::pointer>::type;
+ /// defines a reference to the type iterated over (value_type)
+ using reference =
+ typename std::conditional<std::is_const<BasicJsonType>::value,
+ typename BasicJsonType::const_reference,
+ typename BasicJsonType::reference>::type;
+
+ /// default constructor
+ iter_impl() = default;
+
+ /*!
+ @brief constructor for a given JSON instance
+ @param[in] object pointer to a JSON object for this iterator
+ @pre object != nullptr
+ @post The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ explicit iter_impl(pointer object) noexcept : m_object(object)
+ {
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ {
+ m_it.object_iterator = typename object_t::iterator();
+ break;
+ }
+
+ case value_t::array:
+ {
+ m_it.array_iterator = typename array_t::iterator();
+ break;
+ }
+
+ default:
+ {
+ m_it.primitive_iterator = primitive_iterator_t();
+ break;
+ }
+ }
+ }
+
+ /*!
+ @note The conventional copy constructor and copy assignment are implicitly
+ defined. Combined with the following converting constructor and
+ assignment, they support: (1) copy from iterator to iterator, (2)
+ copy from const iterator to const iterator, and (3) conversion from
+ iterator to const iterator. However conversion from const iterator
+ to iterator is not defined.
+ */
+
+ /*!
+ @brief converting constructor
+ @param[in] other non-const iterator to copy from
+ @note It is not checked whether @a other is initialized.
+ */
+ iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept
+ : m_object(other.m_object), m_it(other.m_it) {}
+
+ /*!
+ @brief converting assignment
+ @param[in,out] other non-const iterator to copy from
+ @return const/non-const iterator
+ @note It is not checked whether @a other is initialized.
+ */
+ iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept
+ {
+ m_object = other.m_object;
+ m_it = other.m_it;
+ return *this;
+ }
+
+ private:
+ /*!
+ @brief set the iterator to the first value
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ void set_begin() noexcept
+ {
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ {
+ m_it.object_iterator = m_object->m_value.object->begin();
+ break;
+ }
+
+ case value_t::array:
+ {
+ m_it.array_iterator = m_object->m_value.array->begin();
+ break;
+ }
+
+ case value_t::null:
+ {
+ // set to end so begin()==end() is true: null is empty
+ m_it.primitive_iterator.set_end();
+ break;
+ }
+
+ default:
+ {
+ m_it.primitive_iterator.set_begin();
+ break;
+ }
+ }
+ }
+
+ /*!
+ @brief set the iterator past the last value
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ void set_end() noexcept
+ {
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ {
+ m_it.object_iterator = m_object->m_value.object->end();
+ break;
+ }
+
+ case value_t::array:
+ {
+ m_it.array_iterator = m_object->m_value.array->end();
+ break;
+ }
+
+ default:
+ {
+ m_it.primitive_iterator.set_end();
+ break;
+ }
+ }
+ }
+
+ public:
+ /*!
+ @brief return a reference to the value pointed to by the iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ reference operator*() const
+ {
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ {
+ assert(m_it.object_iterator != m_object->m_value.object->end());
+ return m_it.object_iterator->second;
+ }
+
+ case value_t::array:
+ {
+ assert(m_it.array_iterator != m_object->m_value.array->end());
+ return *m_it.array_iterator;
+ }
+
+ case value_t::null:
+ JSON_THROW(invalid_iterator::create(214, "cannot get value"));
+
+ default:
+ {
+ if (JSON_LIKELY(m_it.primitive_iterator.is_begin()))
+ {
+ return *m_object;
+ }
+
+ JSON_THROW(invalid_iterator::create(214, "cannot get value"));
+ }
+ }
+ }
+
+ /*!
+ @brief dereference the iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ pointer operator->() const
+ {
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ {
+ assert(m_it.object_iterator != m_object->m_value.object->end());
+ return &(m_it.object_iterator->second);
+ }
+
+ case value_t::array:
+ {
+ assert(m_it.array_iterator != m_object->m_value.array->end());
+ return &*m_it.array_iterator;
+ }
+
+ default:
+ {
+ if (JSON_LIKELY(m_it.primitive_iterator.is_begin()))
+ {
+ return m_object;
+ }
+
+ JSON_THROW(invalid_iterator::create(214, "cannot get value"));
+ }
+ }
+ }
+
+ /*!
+ @brief post-increment (it++)
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl const operator++(int)
+ {
+ auto result = *this;
+ ++(*this);
+ return result;
+ }
+
+ /*!
+ @brief pre-increment (++it)
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl& operator++()
+ {
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ {
+ std::advance(m_it.object_iterator, 1);
+ break;
+ }
+
+ case value_t::array:
+ {
+ std::advance(m_it.array_iterator, 1);
+ break;
+ }
+
+ default:
+ {
+ ++m_it.primitive_iterator;
+ break;
+ }
+ }
+
+ return *this;
+ }
+
+ /*!
+ @brief post-decrement (it--)
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl const operator--(int)
+ {
+ auto result = *this;
+ --(*this);
+ return result;
+ }
+
+ /*!
+ @brief pre-decrement (--it)
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl& operator--()
+ {
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ {
+ std::advance(m_it.object_iterator, -1);
+ break;
+ }
+
+ case value_t::array:
+ {
+ std::advance(m_it.array_iterator, -1);
+ break;
+ }
+
+ default:
+ {
+ --m_it.primitive_iterator;
+ break;
+ }
+ }
+
+ return *this;
+ }
+
+ /*!
+ @brief comparison: equal
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ bool operator==(const iter_impl& other) const
+ {
+ // if objects are not the same, the comparison is undefined
+ if (JSON_UNLIKELY(m_object != other.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers"));
+ }
+
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ return (m_it.object_iterator == other.m_it.object_iterator);
+
+ case value_t::array:
+ return (m_it.array_iterator == other.m_it.array_iterator);
+
+ default:
+ return (m_it.primitive_iterator == other.m_it.primitive_iterator);
+ }
+ }
+
+ /*!
+ @brief comparison: not equal
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ bool operator!=(const iter_impl& other) const
+ {
+ return not operator==(other);
+ }
+
+ /*!
+ @brief comparison: smaller
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ bool operator<(const iter_impl& other) const
+ {
+ // if objects are not the same, the comparison is undefined
+ if (JSON_UNLIKELY(m_object != other.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers"));
+ }
+
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators"));
+
+ case value_t::array:
+ return (m_it.array_iterator < other.m_it.array_iterator);
+
+ default:
+ return (m_it.primitive_iterator < other.m_it.primitive_iterator);
+ }
+ }
+
+ /*!
+ @brief comparison: less than or equal
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ bool operator<=(const iter_impl& other) const
+ {
+ return not other.operator < (*this);
+ }
+
+ /*!
+ @brief comparison: greater than
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ bool operator>(const iter_impl& other) const
+ {
+ return not operator<=(other);
+ }
+
+ /*!
+ @brief comparison: greater than or equal
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ bool operator>=(const iter_impl& other) const
+ {
+ return not operator<(other);
+ }
+
+ /*!
+ @brief add to iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl& operator+=(difference_type i)
+ {
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators"));
+
+ case value_t::array:
+ {
+ std::advance(m_it.array_iterator, i);
+ break;
+ }
+
+ default:
+ {
+ m_it.primitive_iterator += i;
+ break;
+ }
+ }
+
+ return *this;
+ }
+
+ /*!
+ @brief subtract from iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl& operator-=(difference_type i)
+ {
+ return operator+=(-i);
+ }
+
+ /*!
+ @brief add to iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl operator+(difference_type i) const
+ {
+ auto result = *this;
+ result += i;
+ return result;
+ }
+
+ /*!
+ @brief addition of distance and iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ friend iter_impl operator+(difference_type i, const iter_impl& it)
+ {
+ auto result = it;
+ result += i;
+ return result;
+ }
+
+ /*!
+ @brief subtract from iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl operator-(difference_type i) const
+ {
+ auto result = *this;
+ result -= i;
+ return result;
+ }
+
+ /*!
+ @brief return difference
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ difference_type operator-(const iter_impl& other) const
+ {
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators"));
+
+ case value_t::array:
+ return m_it.array_iterator - other.m_it.array_iterator;
+
+ default:
+ return m_it.primitive_iterator - other.m_it.primitive_iterator;
+ }
+ }
+
+ /*!
+ @brief access to successor
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ reference operator[](difference_type n) const
+ {
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators"));
+
+ case value_t::array:
+ return *std::next(m_it.array_iterator, n);
+
+ case value_t::null:
+ JSON_THROW(invalid_iterator::create(214, "cannot get value"));
+
+ default:
+ {
+ if (JSON_LIKELY(m_it.primitive_iterator.get_value() == -n))
+ {
+ return *m_object;
+ }
+
+ JSON_THROW(invalid_iterator::create(214, "cannot get value"));
+ }
+ }
+ }
+
+ /*!
+ @brief return the key of an object iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ typename object_t::key_type key() const
+ {
+ assert(m_object != nullptr);
+
+ if (JSON_LIKELY(m_object->is_object()))
+ {
+ return m_it.object_iterator->first;
+ }
+
+ JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators"));
+ }
+
+ /*!
+ @brief return the value of an iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ reference value() const
+ {
+ return operator*();
+ }
+
+ private:
+ /// associated JSON instance
+ pointer m_object = nullptr;
+ /// the actual iterator of the associated instance
+ internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it = {};
+};
+
+/// proxy class for the iterator_wrapper functions
+template<typename IteratorType> class iteration_proxy
+{
+ private:
+ /// helper class for iteration
+ class iteration_proxy_internal
+ {
+ private:
+ /// the iterator
+ IteratorType anchor;
+ /// an index for arrays (used to create key names)
+ std::size_t array_index = 0;
+
+ public:
+ explicit iteration_proxy_internal(IteratorType it) noexcept : anchor(it) {}
+
+ /// dereference operator (needed for range-based for)
+ iteration_proxy_internal& operator*()
+ {
+ return *this;
+ }
+
+ /// increment operator (needed for range-based for)
+ iteration_proxy_internal& operator++()
+ {
+ ++anchor;
+ ++array_index;
+
+ return *this;
+ }
+
+ /// inequality operator (needed for range-based for)
+ bool operator!=(const iteration_proxy_internal& o) const noexcept
+ {
+ return anchor != o.anchor;
+ }
+
+ /// return key of the iterator
+ std::string key() const
+ {
+ assert(anchor.m_object != nullptr);
+
+ switch (anchor.m_object->type())
+ {
+ // use integer array index as key
+ case value_t::array:
+ return std::to_string(array_index);
+
+ // use key from the object
+ case value_t::object:
+ return anchor.key();
+
+ // use an empty key for all primitive types
+ default:
+ return "";
+ }
+ }
+
+ /// return value of the iterator
+ typename IteratorType::reference value() const
+ {
+ return anchor.value();
+ }
+ };
+
+ /// the container to iterate
+ typename IteratorType::reference container;
+
+ public:
+ /// construct iteration proxy from a container
+ explicit iteration_proxy(typename IteratorType::reference cont)
+ : container(cont) {}
+
+ /// return iterator begin (needed for range-based for)
+ iteration_proxy_internal begin() noexcept
+ {
+ return iteration_proxy_internal(container.begin());
+ }
+
+ /// return iterator end (needed for range-based for)
+ iteration_proxy_internal end() noexcept
+ {
+ return iteration_proxy_internal(container.end());
+ }
+};
+
+/*!
+@brief a template for a reverse iterator class
+
+@tparam Base the base iterator type to reverse. Valid types are @ref
+iterator (to create @ref reverse_iterator) and @ref const_iterator (to
+create @ref const_reverse_iterator).
+
+@requirement The class satisfies the following concept requirements:
+-
+[BidirectionalIterator](http://en.cppreference.com/w/cpp/concept/BidirectionalIterator):
+ The iterator that can be moved can be moved in both directions (i.e.
+ incremented and decremented).
+- [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator):
+ It is possible to write to the pointed-to element (only if @a Base is
+ @ref iterator).
+
+@since version 1.0.0
+*/
+template<typename Base>
+class json_reverse_iterator : public std::reverse_iterator<Base>
+{
+ public:
+ using difference_type = std::ptrdiff_t;
+ /// shortcut to the reverse iterator adapter
+ using base_iterator = std::reverse_iterator<Base>;
+ /// the reference type for the pointed-to element
+ using reference = typename Base::reference;
+
+ /// create reverse iterator from iterator
+ json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept
+ : base_iterator(it) {}
+
+ /// create reverse iterator from base class
+ json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}
+
+ /// post-increment (it++)
+ json_reverse_iterator const operator++(int)
+ {
+ return static_cast<json_reverse_iterator>(base_iterator::operator++(1));
+ }
+
+ /// pre-increment (++it)
+ json_reverse_iterator& operator++()
+ {
+ return static_cast<json_reverse_iterator&>(base_iterator::operator++());
+ }
+
+ /// post-decrement (it--)
+ json_reverse_iterator const operator--(int)
+ {
+ return static_cast<json_reverse_iterator>(base_iterator::operator--(1));
+ }
+
+ /// pre-decrement (--it)
+ json_reverse_iterator& operator--()
+ {
+ return static_cast<json_reverse_iterator&>(base_iterator::operator--());
+ }
+
+ /// add to iterator
+ json_reverse_iterator& operator+=(difference_type i)
+ {
+ return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i));
+ }
+
+ /// add to iterator
+ json_reverse_iterator operator+(difference_type i) const
+ {
+ return static_cast<json_reverse_iterator>(base_iterator::operator+(i));
+ }
+
+ /// subtract from iterator
+ json_reverse_iterator operator-(difference_type i) const
+ {
+ return static_cast<json_reverse_iterator>(base_iterator::operator-(i));
+ }
+
+ /// return difference
+ difference_type operator-(const json_reverse_iterator& other) const
+ {
+ return base_iterator(*this) - base_iterator(other);
+ }
+
+ /// access to successor
+ reference operator[](difference_type n) const
+ {
+ return *(this->operator+(n));
+ }
+
+ /// return the key of an object iterator
+ auto key() const -> decltype(std::declval<Base>().key())
+ {
+ auto it = --this->base();
+ return it.key();
+ }
+
+ /// return the value of an iterator
+ reference value() const
+ {
+ auto it = --this->base();
+ return it.operator * ();
+ }
+};
+
+/////////////////////
+// output adapters //
+/////////////////////
+
+/// abstract output adapter interface
+template<typename CharType> struct output_adapter_protocol
+{
+ virtual void write_character(CharType c) = 0;
+ virtual void write_characters(const CharType* s, std::size_t length) = 0;
+ virtual ~output_adapter_protocol() = default;
+};
+
+/// a type to simplify interfaces
+template<typename CharType>
+using output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>;
+
+/// output adapter for byte vectors
+template<typename CharType>
+class output_vector_adapter : public output_adapter_protocol<CharType>
+{
public:
- static constexpr bool value =
- std::is_integral<decltype(detect(std::declval<T>()))>::value;
+ explicit output_vector_adapter(std::vector<CharType>& vec) : v(vec) {}
+
+ void write_character(CharType c) override
+ {
+ v.push_back(c);
+ }
+
+ void write_characters(const CharType* s, std::size_t length) override
+ {
+ std::copy(s, s + length, std::back_inserter(v));
+ }
+
+ private:
+ std::vector<CharType>& v;
+};
+
+/// output adapter for output streams
+template<typename CharType>
+class output_stream_adapter : public output_adapter_protocol<CharType>
+{
+ public:
+ explicit output_stream_adapter(std::basic_ostream<CharType>& s) : stream(s) {}
+
+ void write_character(CharType c) override
+ {
+ stream.put(c);
+ }
+
+ void write_characters(const CharType* s, std::size_t length) override
+ {
+ stream.write(s, static_cast<std::streamsize>(length));
+ }
+
+ private:
+ std::basic_ostream<CharType>& stream;
+};
+
+/// output adapter for basic_string
+template<typename CharType>
+class output_string_adapter : public output_adapter_protocol<CharType>
+{
+ public:
+ explicit output_string_adapter(std::basic_string<CharType>& s) : str(s) {}
+
+ void write_character(CharType c) override
+ {
+ str.push_back(c);
+ }
+
+ void write_characters(const CharType* s, std::size_t length) override
+ {
+ str.append(s, length);
+ }
+
+ private:
+ std::basic_string<CharType>& str;
+};
+
+template<typename CharType>
+class output_adapter
+{
+ public:
+ output_adapter(std::vector<CharType>& vec)
+ : oa(std::make_shared<output_vector_adapter<CharType>>(vec)) {}
+
+ output_adapter(std::basic_ostream<CharType>& s)
+ : oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}
+
+ output_adapter(std::basic_string<CharType>& s)
+ : oa(std::make_shared<output_string_adapter<CharType>>(s)) {}
+
+ operator output_adapter_t<CharType>()
+ {
+ return oa;
+ }
+
+ private:
+ output_adapter_t<CharType> oa = nullptr;
+};
+
+//////////////////////////////
+// binary reader and writer //
+//////////////////////////////
+
+/*!
+@brief deserialization of CBOR and MessagePack values
+*/
+template<typename BasicJsonType>
+class binary_reader
+{
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+
+ public:
+ /*!
+ @brief create a binary reader
+
+ @param[in] adapter input adapter to read from
+ */
+ explicit binary_reader(input_adapter_t adapter) : ia(std::move(adapter))
+ {
+ assert(ia);
+ }
+
+ /*!
+ @brief create a JSON value from CBOR input
+
+ @param[in] strict whether to expect the input to be consumed completed
+ @return JSON value created from CBOR input
+
+ @throw parse_error.110 if input ended unexpectedly or the end of file was
+ not reached when @a strict was set to true
+ @throw parse_error.112 if unsupported byte was read
+ */
+ BasicJsonType parse_cbor(const bool strict)
+ {
+ const auto res = parse_cbor_internal();
+ if (strict)
+ {
+ get();
+ check_eof(true);
+ }
+ return res;
+ }
+
+ /*!
+ @brief create a JSON value from MessagePack input
+
+ @param[in] strict whether to expect the input to be consumed completed
+ @return JSON value created from MessagePack input
+
+ @throw parse_error.110 if input ended unexpectedly or the end of file was
+ not reached when @a strict was set to true
+ @throw parse_error.112 if unsupported byte was read
+ */
+ BasicJsonType parse_msgpack(const bool strict)
+ {
+ const auto res = parse_msgpack_internal();
+ if (strict)
+ {
+ get();
+ check_eof(true);
+ }
+ return res;
+ }
+
+ /*!
+ @brief determine system byte order
+
+ @return true if and only if system's byte order is little endian
+
+ @note from http://stackoverflow.com/a/1001328/266378
+ */
+ static constexpr bool little_endianess(int num = 1) noexcept
+ {
+ return (*reinterpret_cast<char*>(&num) == 1);
+ }
+
+ private:
+ /*!
+ @param[in] get_char whether a new character should be retrieved from the
+ input (true, default) or whether the last read
+ character should be considered instead
+ */
+ BasicJsonType parse_cbor_internal(const bool get_char = true)
+ {
+ switch (get_char ? get() : current)
+ {
+ // EOF
+ case std::char_traits<char>::eof():
+ JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input"));
+
+ // Integer 0x00..0x17 (0..23)
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0A:
+ case 0x0B:
+ case 0x0C:
+ case 0x0D:
+ case 0x0E:
+ case 0x0F:
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ return static_cast<number_unsigned_t>(current);
+
+ case 0x18: // Unsigned integer (one-byte uint8_t follows)
+ return get_number<uint8_t>();
+
+ case 0x19: // Unsigned integer (two-byte uint16_t follows)
+ return get_number<uint16_t>();
+
+ case 0x1A: // Unsigned integer (four-byte uint32_t follows)
+ return get_number<uint32_t>();
+
+ case 0x1B: // Unsigned integer (eight-byte uint64_t follows)
+ return get_number<uint64_t>();
+
+ // Negative integer -1-0x00..-1-0x17 (-1..-24)
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ case 0x2C:
+ case 0x2D:
+ case 0x2E:
+ case 0x2F:
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ return static_cast<int8_t>(0x20 - 1 - current);
+
+ case 0x38: // Negative integer (one-byte uint8_t follows)
+ {
+ // must be uint8_t !
+ return static_cast<number_integer_t>(-1) - get_number<uint8_t>();
+ }
+
+ case 0x39: // Negative integer -1-n (two-byte uint16_t follows)
+ {
+ return static_cast<number_integer_t>(-1) - get_number<uint16_t>();
+ }
+
+ case 0x3A: // Negative integer -1-n (four-byte uint32_t follows)
+ {
+ return static_cast<number_integer_t>(-1) - get_number<uint32_t>();
+ }
+
+ case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows)
+ {
+ return static_cast<number_integer_t>(-1) -
+ static_cast<number_integer_t>(get_number<uint64_t>());
+ }
+
+ // UTF-8 string (0x00..0x17 bytes follow)
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
+ case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
+ case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)
+ case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)
+ case 0x7F: // UTF-8 string (indefinite length)
+ {
+ return get_cbor_string();
+ }
+
+ // array (0x00..0x17 data items follow)
+ case 0x80:
+ case 0x81:
+ case 0x82:
+ case 0x83:
+ case 0x84:
+ case 0x85:
+ case 0x86:
+ case 0x87:
+ case 0x88:
+ case 0x89:
+ case 0x8A:
+ case 0x8B:
+ case 0x8C:
+ case 0x8D:
+ case 0x8E:
+ case 0x8F:
+ case 0x90:
+ case 0x91:
+ case 0x92:
+ case 0x93:
+ case 0x94:
+ case 0x95:
+ case 0x96:
+ case 0x97:
+ {
+ return get_cbor_array(current & 0x1F);
+ }
+
+ case 0x98: // array (one-byte uint8_t for n follows)
+ {
+ return get_cbor_array(get_number<uint8_t>());
+ }
+
+ case 0x99: // array (two-byte uint16_t for n follow)
+ {
+ return get_cbor_array(get_number<uint16_t>());
+ }
+
+ case 0x9A: // array (four-byte uint32_t for n follow)
+ {
+ return get_cbor_array(get_number<uint32_t>());
+ }
+
+ case 0x9B: // array (eight-byte uint64_t for n follow)
+ {
+ return get_cbor_array(get_number<uint64_t>());
+ }
+
+ case 0x9F: // array (indefinite length)
+ {
+ BasicJsonType result = value_t::array;
+ while (get() != 0xFF)
+ {
+ result.push_back(parse_cbor_internal(false));
+ }
+ return result;
+ }
+
+ // map (0x00..0x17 pairs of data items follow)
+ case 0xA0:
+ case 0xA1:
+ case 0xA2:
+ case 0xA3:
+ case 0xA4:
+ case 0xA5:
+ case 0xA6:
+ case 0xA7:
+ case 0xA8:
+ case 0xA9:
+ case 0xAA:
+ case 0xAB:
+ case 0xAC:
+ case 0xAD:
+ case 0xAE:
+ case 0xAF:
+ case 0xB0:
+ case 0xB1:
+ case 0xB2:
+ case 0xB3:
+ case 0xB4:
+ case 0xB5:
+ case 0xB6:
+ case 0xB7:
+ {
+ return get_cbor_object(current & 0x1F);
+ }
+
+ case 0xB8: // map (one-byte uint8_t for n follows)
+ {
+ return get_cbor_object(get_number<uint8_t>());
+ }
+
+ case 0xB9: // map (two-byte uint16_t for n follow)
+ {
+ return get_cbor_object(get_number<uint16_t>());
+ }
+
+ case 0xBA: // map (four-byte uint32_t for n follow)
+ {
+ return get_cbor_object(get_number<uint32_t>());
+ }
+
+ case 0xBB: // map (eight-byte uint64_t for n follow)
+ {
+ return get_cbor_object(get_number<uint64_t>());
+ }
+
+ case 0xBF: // map (indefinite length)
+ {
+ BasicJsonType result = value_t::object;
+ while (get() != 0xFF)
+ {
+ auto key = get_cbor_string();
+ result[key] = parse_cbor_internal();
+ }
+ return result;
+ }
+
+ case 0xF4: // false
+ {
+ return false;
+ }
+
+ case 0xF5: // true
+ {
+ return true;
+ }
+
+ case 0xF6: // null
+ {
+ return value_t::null;
+ }
+
+ case 0xF9: // Half-Precision Float (two-byte IEEE 754)
+ {
+ const int byte1 = get();
+ check_eof();
+ const int byte2 = get();
+ check_eof();
+
+ // code from RFC 7049, Appendix D, Figure 3:
+ // As half-precision floating-point numbers were only added
+ // to IEEE 754 in 2008, today's programming platforms often
+ // still only have limited support for them. It is very
+ // easy to include at least decoding support for them even
+ // without such support. An example of a small decoder for
+ // half-precision floating-point numbers in the C language
+ // is shown in Fig. 3.
+ const int half = (byte1 << 8) + byte2;
+ const int exp = (half >> 10) & 0x1F;
+ const int mant = half & 0x3FF;
+ double val;
+ if (exp == 0)
+ {
+ val = std::ldexp(mant, -24);
+ }
+ else if (exp != 31)
+ {
+ val = std::ldexp(mant + 1024, exp - 25);
+ }
+ else
+ {
+ val = (mant == 0) ? std::numeric_limits<double>::infinity()
+ : std::numeric_limits<double>::quiet_NaN();
+ }
+ return (half & 0x8000) != 0 ? -val : val;
+ }
+
+ case 0xFA: // Single-Precision Float (four-byte IEEE 754)
+ {
+ return get_number<float>();
+ }
+
+ case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
+ {
+ return get_number<double>();
+ }
+
+ default: // anything else (0xFF is handled inside the other types)
+ {
+ std::stringstream ss;
+ ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current;
+ JSON_THROW(parse_error::create(112, chars_read, "error reading CBOR; last byte: 0x" + ss.str()));
+ }
+ }
+ }
+
+ BasicJsonType parse_msgpack_internal()
+ {
+ switch (get())
+ {
+ // EOF
+ case std::char_traits<char>::eof():
+ JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input"));
+
+ // positive fixint
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0A:
+ case 0x0B:
+ case 0x0C:
+ case 0x0D:
+ case 0x0E:
+ case 0x0F:
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ case 0x18:
+ case 0x19:
+ case 0x1A:
+ case 0x1B:
+ case 0x1C:
+ case 0x1D:
+ case 0x1E:
+ case 0x1F:
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ case 0x2C:
+ case 0x2D:
+ case 0x2E:
+ case 0x2F:
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ case 0x3A:
+ case 0x3B:
+ case 0x3C:
+ case 0x3D:
+ case 0x3E:
+ case 0x3F:
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4A:
+ case 0x4B:
+ case 0x4C:
+ case 0x4D:
+ case 0x4E:
+ case 0x4F:
+ case 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ case 0x54:
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ case 0x58:
+ case 0x59:
+ case 0x5A:
+ case 0x5B:
+ case 0x5C:
+ case 0x5D:
+ case 0x5E:
+ case 0x5F:
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ case 0x78:
+ case 0x79:
+ case 0x7A:
+ case 0x7B:
+ case 0x7C:
+ case 0x7D:
+ case 0x7E:
+ case 0x7F:
+ return static_cast<number_unsigned_t>(current);
+
+ // fixmap
+ case 0x80:
+ case 0x81:
+ case 0x82:
+ case 0x83:
+ case 0x84:
+ case 0x85:
+ case 0x86:
+ case 0x87:
+ case 0x88:
+ case 0x89:
+ case 0x8A:
+ case 0x8B:
+ case 0x8C:
+ case 0x8D:
+ case 0x8E:
+ case 0x8F:
+ {
+ return get_msgpack_object(current & 0x0F);
+ }
+
+ // fixarray
+ case 0x90:
+ case 0x91:
+ case 0x92:
+ case 0x93:
+ case 0x94:
+ case 0x95:
+ case 0x96:
+ case 0x97:
+ case 0x98:
+ case 0x99:
+ case 0x9A:
+ case 0x9B:
+ case 0x9C:
+ case 0x9D:
+ case 0x9E:
+ case 0x9F:
+ {
+ return get_msgpack_array(current & 0x0F);
+ }
+
+ // fixstr
+ case 0xA0:
+ case 0xA1:
+ case 0xA2:
+ case 0xA3:
+ case 0xA4:
+ case 0xA5:
+ case 0xA6:
+ case 0xA7:
+ case 0xA8:
+ case 0xA9:
+ case 0xAA:
+ case 0xAB:
+ case 0xAC:
+ case 0xAD:
+ case 0xAE:
+ case 0xAF:
+ case 0xB0:
+ case 0xB1:
+ case 0xB2:
+ case 0xB3:
+ case 0xB4:
+ case 0xB5:
+ case 0xB6:
+ case 0xB7:
+ case 0xB8:
+ case 0xB9:
+ case 0xBA:
+ case 0xBB:
+ case 0xBC:
+ case 0xBD:
+ case 0xBE:
+ case 0xBF:
+ return get_msgpack_string();
+
+ case 0xC0: // nil
+ return value_t::null;
+
+ case 0xC2: // false
+ return false;
+
+ case 0xC3: // true
+ return true;
+
+ case 0xCA: // float 32
+ return get_number<float>();
+
+ case 0xCB: // float 64
+ return get_number<double>();
+
+ case 0xCC: // uint 8
+ return get_number<uint8_t>();
+
+ case 0xCD: // uint 16
+ return get_number<uint16_t>();
+
+ case 0xCE: // uint 32
+ return get_number<uint32_t>();
+
+ case 0xCF: // uint 64
+ return get_number<uint64_t>();
+
+ case 0xD0: // int 8
+ return get_number<int8_t>();
+
+ case 0xD1: // int 16
+ return get_number<int16_t>();
+
+ case 0xD2: // int 32
+ return get_number<int32_t>();
+
+ case 0xD3: // int 64
+ return get_number<int64_t>();
+
+ case 0xD9: // str 8
+ case 0xDA: // str 16
+ case 0xDB: // str 32
+ return get_msgpack_string();
+
+ case 0xDC: // array 16
+ {
+ return get_msgpack_array(get_number<uint16_t>());
+ }
+
+ case 0xDD: // array 32
+ {
+ return get_msgpack_array(get_number<uint32_t>());
+ }
+
+ case 0xDE: // map 16
+ {
+ return get_msgpack_object(get_number<uint16_t>());
+ }
+
+ case 0xDF: // map 32
+ {
+ return get_msgpack_object(get_number<uint32_t>());
+ }
+
+ // positive fixint
+ case 0xE0:
+ case 0xE1:
+ case 0xE2:
+ case 0xE3:
+ case 0xE4:
+ case 0xE5:
+ case 0xE6:
+ case 0xE7:
+ case 0xE8:
+ case 0xE9:
+ case 0xEA:
+ case 0xEB:
+ case 0xEC:
+ case 0xED:
+ case 0xEE:
+ case 0xEF:
+ case 0xF0:
+ case 0xF1:
+ case 0xF2:
+ case 0xF3:
+ case 0xF4:
+ case 0xF5:
+ case 0xF6:
+ case 0xF7:
+ case 0xF8:
+ case 0xF9:
+ case 0xFA:
+ case 0xFB:
+ case 0xFC:
+ case 0xFD:
+ case 0xFE:
+ case 0xFF:
+ return static_cast<int8_t>(current);
+
+ default: // anything else
+ {
+ std::stringstream ss;
+ ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current;
+ JSON_THROW(parse_error::create(112, chars_read,
+ "error reading MessagePack; last byte: 0x" + ss.str()));
+ }
+ }
+ }
+
+ /*!
+ @brief get next character from the input
+
+ This function provides the interface to the used input adapter. It does
+ not throw in case the input reached EOF, but returns a -'ve valued
+ `std::char_traits<char>::eof()` in that case.
+
+ @return character read from the input
+ */
+ int get()
+ {
+ ++chars_read;
+ return (current = ia->get_character());
+ }
+
+ /*
+ @brief read a number from the input
+
+ @tparam NumberType the type of the number
+
+ @return number of type @a NumberType
+
+ @note This function needs to respect the system's endianess, because
+ bytes in CBOR and MessagePack are stored in network order (big
+ endian) and therefore need reordering on little endian systems.
+
+ @throw parse_error.110 if input has less than `sizeof(NumberType)` bytes
+ */
+ template<typename NumberType> NumberType get_number()
+ {
+ // step 1: read input into array with system's byte order
+ std::array<uint8_t, sizeof(NumberType)> vec;
+ for (std::size_t i = 0; i < sizeof(NumberType); ++i)
+ {
+ get();
+ check_eof();
+
+ // reverse byte order prior to conversion if necessary
+ if (is_little_endian)
+ {
+ vec[sizeof(NumberType) - i - 1] = static_cast<uint8_t>(current);
+ }
+ else
+ {
+ vec[i] = static_cast<uint8_t>(current); // LCOV_EXCL_LINE
+ }
+ }
+
+ // step 2: convert array into number of type T and return
+ NumberType result;
+ std::memcpy(&result, vec.data(), sizeof(NumberType));
+ return result;
+ }
+
+ /*!
+ @brief create a string by reading characters from the input
+
+ @param[in] len number of bytes to read
+
+ @note We can not reserve @a len bytes for the result, because @a len
+ may be too large. Usually, @ref check_eof() detects the end of
+ the input before we run out of string memory.
+
+ @return string created by reading @a len bytes
+
+ @throw parse_error.110 if input has less than @a len bytes
+ */
+ template<typename NumberType>
+ std::string get_string(const NumberType len)
+ {
+ std::string result;
+ std::generate_n(std::back_inserter(result), len, [this]()
+ {
+ get();
+ check_eof();
+ return static_cast<char>(current);
+ });
+ return result;
+ }
+
+ /*!
+ @brief reads a CBOR string
+
+ This function first reads starting bytes to determine the expected
+ string length and then copies this number of bytes into a string.
+ Additionally, CBOR's strings with indefinite lengths are supported.
+
+ @return string
+
+ @throw parse_error.110 if input ended
+ @throw parse_error.113 if an unexpected byte is read
+ */
+ std::string get_cbor_string()
+ {
+ check_eof();
+
+ switch (current)
+ {
+ // UTF-8 string (0x00..0x17 bytes follow)
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ {
+ return get_string(current & 0x1F);
+ }
+
+ case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
+ {
+ return get_string(get_number<uint8_t>());
+ }
+
+ case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
+ {
+ return get_string(get_number<uint16_t>());
+ }
+
+ case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)
+ {
+ return get_string(get_number<uint32_t>());
+ }
+
+ case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)
+ {
+ return get_string(get_number<uint64_t>());
+ }
+
+ case 0x7F: // UTF-8 string (indefinite length)
+ {
+ std::string result;
+ while (get() != 0xFF)
+ {
+ check_eof();
+ result.push_back(static_cast<char>(current));
+ }
+ return result;
+ }
+
+ default:
+ {
+ std::stringstream ss;
+ ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current;
+ JSON_THROW(parse_error::create(113, chars_read, "expected a CBOR string; last byte: 0x" + ss.str()));
+ }
+ }
+ }
+
+ template<typename NumberType>
+ BasicJsonType get_cbor_array(const NumberType len)
+ {
+ BasicJsonType result = value_t::array;
+ std::generate_n(std::back_inserter(*result.m_value.array), len, [this]()
+ {
+ return parse_cbor_internal();
+ });
+ return result;
+ }
+
+ template<typename NumberType>
+ BasicJsonType get_cbor_object(const NumberType len)
+ {
+ BasicJsonType result = value_t::object;
+ std::generate_n(std::inserter(*result.m_value.object,
+ result.m_value.object->end()),
+ len, [this]()
+ {
+ get();
+ auto key = get_cbor_string();
+ auto val = parse_cbor_internal();
+ return std::make_pair(std::move(key), std::move(val));
+ });
+ return result;
+ }
+
+ /*!
+ @brief reads a MessagePack string
+
+ This function first reads starting bytes to determine the expected
+ string length and then copies this number of bytes into a string.
+
+ @return string
+
+ @throw parse_error.110 if input ended
+ @throw parse_error.113 if an unexpected byte is read
+ */
+ std::string get_msgpack_string()
+ {
+ check_eof();
+
+ switch (current)
+ {
+ // fixstr
+ case 0xA0:
+ case 0xA1:
+ case 0xA2:
+ case 0xA3:
+ case 0xA4:
+ case 0xA5:
+ case 0xA6:
+ case 0xA7:
+ case 0xA8:
+ case 0xA9:
+ case 0xAA:
+ case 0xAB:
+ case 0xAC:
+ case 0xAD:
+ case 0xAE:
+ case 0xAF:
+ case 0xB0:
+ case 0xB1:
+ case 0xB2:
+ case 0xB3:
+ case 0xB4:
+ case 0xB5:
+ case 0xB6:
+ case 0xB7:
+ case 0xB8:
+ case 0xB9:
+ case 0xBA:
+ case 0xBB:
+ case 0xBC:
+ case 0xBD:
+ case 0xBE:
+ case 0xBF:
+ {
+ return get_string(current & 0x1F);
+ }
+
+ case 0xD9: // str 8
+ {
+ return get_string(get_number<uint8_t>());
+ }
+
+ case 0xDA: // str 16
+ {
+ return get_string(get_number<uint16_t>());
+ }
+
+ case 0xDB: // str 32
+ {
+ return get_string(get_number<uint32_t>());
+ }
+
+ default:
+ {
+ std::stringstream ss;
+ ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current;
+ JSON_THROW(parse_error::create(113, chars_read,
+ "expected a MessagePack string; last byte: 0x" + ss.str()));
+ }
+ }
+ }
+
+ template<typename NumberType>
+ BasicJsonType get_msgpack_array(const NumberType len)
+ {
+ BasicJsonType result = value_t::array;
+ std::generate_n(std::back_inserter(*result.m_value.array), len, [this]()
+ {
+ return parse_msgpack_internal();
+ });
+ return result;
+ }
+
+ template<typename NumberType>
+ BasicJsonType get_msgpack_object(const NumberType len)
+ {
+ BasicJsonType result = value_t::object;
+ std::generate_n(std::inserter(*result.m_value.object,
+ result.m_value.object->end()),
+ len, [this]()
+ {
+ get();
+ auto key = get_msgpack_string();
+ auto val = parse_msgpack_internal();
+ return std::make_pair(std::move(key), std::move(val));
+ });
+ return result;
+ }
+
+ /*!
+ @brief check if input ended
+ @throw parse_error.110 if input ended
+ */
+ void check_eof(const bool expect_eof = false) const
+ {
+ if (expect_eof)
+ {
+ if (JSON_UNLIKELY(current != std::char_traits<char>::eof()))
+ {
+ JSON_THROW(parse_error::create(110, chars_read, "expected end of input"));
+ }
+ }
+ else
+ {
+ if (JSON_UNLIKELY(current == std::char_traits<char>::eof()))
+ {
+ JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input"));
+ }
+ }
+ }
+
+ private:
+ /// input adapter
+ input_adapter_t ia = nullptr;
+
+ /// the current character
+ int current = std::char_traits<char>::eof();
+
+ /// the number of characters read
+ std::size_t chars_read = 0;
+
+ /// whether we can assume little endianess
+ const bool is_little_endian = little_endianess();
+};
+
+/*!
+@brief serialization to CBOR and MessagePack values
+*/
+template<typename BasicJsonType, typename CharType>
+class binary_writer
+{
+ public:
+ /*!
+ @brief create a binary writer
+
+ @param[in] adapter output adapter to write to
+ */
+ explicit binary_writer(output_adapter_t<CharType> adapter) : oa(adapter)
+ {
+ assert(oa);
+ }
+
+ /*!
+ @brief[in] j JSON value to serialize
+ */
+ void write_cbor(const BasicJsonType& j)
+ {
+ switch (j.type())
+ {
+ case value_t::null:
+ {
+ oa->write_character(static_cast<CharType>(0xF6));
+ break;
+ }
+
+ case value_t::boolean:
+ {
+ oa->write_character(j.m_value.boolean
+ ? static_cast<CharType>(0xF5)
+ : static_cast<CharType>(0xF4));
+ break;
+ }
+
+ case value_t::number_integer:
+ {
+ if (j.m_value.number_integer >= 0)
+ {
+ // CBOR does not differentiate between positive signed
+ // integers and unsigned integers. Therefore, we used the
+ // code from the value_t::number_unsigned case here.
+ if (j.m_value.number_integer <= 0x17)
+ {
+ write_number(static_cast<uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer <= (std::numeric_limits<uint8_t>::max)())
+ {
+ oa->write_character(static_cast<CharType>(0x18));
+ write_number(static_cast<uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer <= (std::numeric_limits<uint16_t>::max)())
+ {
+ oa->write_character(static_cast<CharType>(0x19));
+ write_number(static_cast<uint16_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer <= (std::numeric_limits<uint32_t>::max)())
+ {
+ oa->write_character(static_cast<CharType>(0x1A));
+ write_number(static_cast<uint32_t>(j.m_value.number_integer));
+ }
+ else
+ {
+ oa->write_character(static_cast<CharType>(0x1B));
+ write_number(static_cast<uint64_t>(j.m_value.number_integer));
+ }
+ }
+ else
+ {
+ // The conversions below encode the sign in the first
+ // byte, and the value is converted to a positive number.
+ const auto positive_number = -1 - j.m_value.number_integer;
+ if (j.m_value.number_integer >= -24)
+ {
+ write_number(static_cast<uint8_t>(0x20 + positive_number));
+ }
+ else if (positive_number <= (std::numeric_limits<uint8_t>::max)())
+ {
+ oa->write_character(static_cast<CharType>(0x38));
+ write_number(static_cast<uint8_t>(positive_number));
+ }
+ else if (positive_number <= (std::numeric_limits<uint16_t>::max)())
+ {
+ oa->write_character(static_cast<CharType>(0x39));
+ write_number(static_cast<uint16_t>(positive_number));
+ }
+ else if (positive_number <= (std::numeric_limits<uint32_t>::max)())
+ {
+ oa->write_character(static_cast<CharType>(0x3A));
+ write_number(static_cast<uint32_t>(positive_number));
+ }
+ else
+ {
+ oa->write_character(static_cast<CharType>(0x3B));
+ write_number(static_cast<uint64_t>(positive_number));
+ }
+ }
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ if (j.m_value.number_unsigned <= 0x17)
+ {
+ write_number(static_cast<uint8_t>(j.m_value.number_unsigned));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
+ {
+ oa->write_character(static_cast<CharType>(0x18));
+ write_number(static_cast<uint8_t>(j.m_value.number_unsigned));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
+ {
+ oa->write_character(static_cast<CharType>(0x19));
+ write_number(static_cast<uint16_t>(j.m_value.number_unsigned));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
+ {
+ oa->write_character(static_cast<CharType>(0x1A));
+ write_number(static_cast<uint32_t>(j.m_value.number_unsigned));
+ }
+ else
+ {
+ oa->write_character(static_cast<CharType>(0x1B));
+ write_number(static_cast<uint64_t>(j.m_value.number_unsigned));
+ }
+ break;
+ }
+
+ case value_t::number_float: // Double-Precision Float
+ {
+ oa->write_character(static_cast<CharType>(0xFB));
+ write_number(j.m_value.number_float);
+ break;
+ }
+
+ case value_t::string:
+ {
+ // step 1: write control byte and the string length
+ const auto N = j.m_value.string->size();
+ if (N <= 0x17)
+ {
+ write_number(static_cast<uint8_t>(0x60 + N));
+ }
+ else if (N <= 0xFF)
+ {
+ oa->write_character(static_cast<CharType>(0x78));
+ write_number(static_cast<uint8_t>(N));
+ }
+ else if (N <= 0xFFFF)
+ {
+ oa->write_character(static_cast<CharType>(0x79));
+ write_number(static_cast<uint16_t>(N));
+ }
+ else if (N <= 0xFFFFFFFF)
+ {
+ oa->write_character(static_cast<CharType>(0x7A));
+ write_number(static_cast<uint32_t>(N));
+ }
+ // LCOV_EXCL_START
+ else if (N <= 0xFFFFFFFFFFFFFFFF)
+ {
+ oa->write_character(static_cast<CharType>(0x7B));
+ write_number(static_cast<uint64_t>(N));
+ }
+ // LCOV_EXCL_STOP
+
+ // step 2: write the string
+ oa->write_characters(
+ reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
+ j.m_value.string->size());
+ break;
+ }
+
+ case value_t::array:
+ {
+ // step 1: write control byte and the array size
+ const auto N = j.m_value.array->size();
+ if (N <= 0x17)
+ {
+ write_number(static_cast<uint8_t>(0x80 + N));
+ }
+ else if (N <= 0xFF)
+ {
+ oa->write_character(static_cast<CharType>(0x98));
+ write_number(static_cast<uint8_t>(N));
+ }
+ else if (N <= 0xFFFF)
+ {
+ oa->write_character(static_cast<CharType>(0x99));
+ write_number(static_cast<uint16_t>(N));
+ }
+ else if (N <= 0xFFFFFFFF)
+ {
+ oa->write_character(static_cast<CharType>(0x9A));
+ write_number(static_cast<uint32_t>(N));
+ }
+ // LCOV_EXCL_START
+ else if (N <= 0xFFFFFFFFFFFFFFFF)
+ {
+ oa->write_character(static_cast<CharType>(0x9B));
+ write_number(static_cast<uint64_t>(N));
+ }
+ // LCOV_EXCL_STOP
+
+ // step 2: write each element
+ for (const auto& el : *j.m_value.array)
+ {
+ write_cbor(el);
+ }
+ break;
+ }
+
+ case value_t::object:
+ {
+ // step 1: write control byte and the object size
+ const auto N = j.m_value.object->size();
+ if (N <= 0x17)
+ {
+ write_number(static_cast<uint8_t>(0xA0 + N));
+ }
+ else if (N <= 0xFF)
+ {
+ oa->write_character(static_cast<CharType>(0xB8));
+ write_number(static_cast<uint8_t>(N));
+ }
+ else if (N <= 0xFFFF)
+ {
+ oa->write_character(static_cast<CharType>(0xB9));
+ write_number(static_cast<uint16_t>(N));
+ }
+ else if (N <= 0xFFFFFFFF)
+ {
+ oa->write_character(static_cast<CharType>(0xBA));
+ write_number(static_cast<uint32_t>(N));
+ }
+ // LCOV_EXCL_START
+ else if (N <= 0xFFFFFFFFFFFFFFFF)
+ {
+ oa->write_character(static_cast<CharType>(0xBB));
+ write_number(static_cast<uint64_t>(N));
+ }
+ // LCOV_EXCL_STOP
+
+ // step 2: write each element
+ for (const auto& el : *j.m_value.object)
+ {
+ write_cbor(el.first);
+ write_cbor(el.second);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ /*!
+ @brief[in] j JSON value to serialize
+ */
+ void write_msgpack(const BasicJsonType& j)
+ {
+ switch (j.type())
+ {
+ case value_t::null: // nil
+ {
+ oa->write_character(static_cast<CharType>(0xC0));
+ break;
+ }
+
+ case value_t::boolean: // true and false
+ {
+ oa->write_character(j.m_value.boolean
+ ? static_cast<CharType>(0xC3)
+ : static_cast<CharType>(0xC2));
+ break;
+ }
+
+ case value_t::number_integer:
+ {
+ if (j.m_value.number_integer >= 0)
+ {
+ // MessagePack does not differentiate between positive
+ // signed integers and unsigned integers. Therefore, we used
+ // the code from the value_t::number_unsigned case here.
+ if (j.m_value.number_unsigned < 128)
+ {
+ // positive fixnum
+ write_number(static_cast<uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
+ {
+ // uint 8
+ oa->write_character(static_cast<CharType>(0xCC));
+ write_number(static_cast<uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
+ {
+ // uint 16
+ oa->write_character(static_cast<CharType>(0xCD));
+ write_number(static_cast<uint16_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
+ {
+ // uint 32
+ oa->write_character(static_cast<CharType>(0xCE));
+ write_number(static_cast<uint32_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)())
+ {
+ // uint 64
+ oa->write_character(static_cast<CharType>(0xCF));
+ write_number(static_cast<uint64_t>(j.m_value.number_integer));
+ }
+ }
+ else
+ {
+ if (j.m_value.number_integer >= -32)
+ {
+ // negative fixnum
+ write_number(static_cast<int8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer >= (std::numeric_limits<int8_t>::min)() and
+ j.m_value.number_integer <= (std::numeric_limits<int8_t>::max)())
+ {
+ // int 8
+ oa->write_character(static_cast<CharType>(0xD0));
+ write_number(static_cast<int8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer >= (std::numeric_limits<int16_t>::min)() and
+ j.m_value.number_integer <= (std::numeric_limits<int16_t>::max)())
+ {
+ // int 16
+ oa->write_character(static_cast<CharType>(0xD1));
+ write_number(static_cast<int16_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer >= (std::numeric_limits<int32_t>::min)() and
+ j.m_value.number_integer <= (std::numeric_limits<int32_t>::max)())
+ {
+ // int 32
+ oa->write_character(static_cast<CharType>(0xD2));
+ write_number(static_cast<int32_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer >= (std::numeric_limits<int64_t>::min)() and
+ j.m_value.number_integer <= (std::numeric_limits<int64_t>::max)())
+ {
+ // int 64
+ oa->write_character(static_cast<CharType>(0xD3));
+ write_number(static_cast<int64_t>(j.m_value.number_integer));
+ }
+ }
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ if (j.m_value.number_unsigned < 128)
+ {
+ // positive fixnum
+ write_number(static_cast<uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
+ {
+ // uint 8
+ oa->write_character(static_cast<CharType>(0xCC));
+ write_number(static_cast<uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
+ {
+ // uint 16
+ oa->write_character(static_cast<CharType>(0xCD));
+ write_number(static_cast<uint16_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
+ {
+ // uint 32
+ oa->write_character(static_cast<CharType>(0xCE));
+ write_number(static_cast<uint32_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)())
+ {
+ // uint 64
+ oa->write_character(static_cast<CharType>(0xCF));
+ write_number(static_cast<uint64_t>(j.m_value.number_integer));
+ }
+ break;
+ }
+
+ case value_t::number_float: // float 64
+ {
+ oa->write_character(static_cast<CharType>(0xCB));
+ write_number(j.m_value.number_float);
+ break;
+ }
+
+ case value_t::string:
+ {
+ // step 1: write control byte and the string length
+ const auto N = j.m_value.string->size();
+ if (N <= 31)
+ {
+ // fixstr
+ write_number(static_cast<uint8_t>(0xA0 | N));
+ }
+ else if (N <= 255)
+ {
+ // str 8
+ oa->write_character(static_cast<CharType>(0xD9));
+ write_number(static_cast<uint8_t>(N));
+ }
+ else if (N <= 65535)
+ {
+ // str 16
+ oa->write_character(static_cast<CharType>(0xDA));
+ write_number(static_cast<uint16_t>(N));
+ }
+ else if (N <= 4294967295)
+ {
+ // str 32
+ oa->write_character(static_cast<CharType>(0xDB));
+ write_number(static_cast<uint32_t>(N));
+ }
+
+ // step 2: write the string
+ oa->write_characters(
+ reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
+ j.m_value.string->size());
+ break;
+ }
+
+ case value_t::array:
+ {
+ // step 1: write control byte and the array size
+ const auto N = j.m_value.array->size();
+ if (N <= 15)
+ {
+ // fixarray
+ write_number(static_cast<uint8_t>(0x90 | N));
+ }
+ else if (N <= 0xFFFF)
+ {
+ // array 16
+ oa->write_character(static_cast<CharType>(0xDC));
+ write_number(static_cast<uint16_t>(N));
+ }
+ else if (N <= 0xFFFFFFFF)
+ {
+ // array 32
+ oa->write_character(static_cast<CharType>(0xDD));
+ write_number(static_cast<uint32_t>(N));
+ }
+
+ // step 2: write each element
+ for (const auto& el : *j.m_value.array)
+ {
+ write_msgpack(el);
+ }
+ break;
+ }
+
+ case value_t::object:
+ {
+ // step 1: write control byte and the object size
+ const auto N = j.m_value.object->size();
+ if (N <= 15)
+ {
+ // fixmap
+ write_number(static_cast<uint8_t>(0x80 | (N & 0xF)));
+ }
+ else if (N <= 65535)
+ {
+ // map 16
+ oa->write_character(static_cast<CharType>(0xDE));
+ write_number(static_cast<uint16_t>(N));
+ }
+ else if (N <= 4294967295)
+ {
+ // map 32
+ oa->write_character(static_cast<CharType>(0xDF));
+ write_number(static_cast<uint32_t>(N));
+ }
+
+ // step 2: write each element
+ for (const auto& el : *j.m_value.object)
+ {
+ write_msgpack(el.first);
+ write_msgpack(el.second);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ private:
+ /*
+ @brief write a number to output input
+
+ @param[in] n number of type @a NumberType
+ @tparam NumberType the type of the number
+
+ @note This function needs to respect the system's endianess, because bytes
+ in CBOR and MessagePack are stored in network order (big endian) and
+ therefore need reordering on little endian systems.
+ */
+ template<typename NumberType> void write_number(NumberType n)
+ {
+ // step 1: write number to array of length NumberType
+ std::array<CharType, sizeof(NumberType)> vec;
+ std::memcpy(vec.data(), &n, sizeof(NumberType));
+
+ // step 2: write array to output (with possible reordering)
+ if (is_little_endian)
+ {
+ // reverse byte order prior to conversion if necessary
+ std::reverse(vec.begin(), vec.end());
+ }
+
+ oa->write_characters(vec.data(), sizeof(NumberType));
+ }
+
+ private:
+ /// whether we can assume little endianess
+ const bool is_little_endian = binary_reader<BasicJsonType>::little_endianess();
+
+ /// the output
+ output_adapter_t<CharType> oa = nullptr;
+};
+
+///////////////////
+// serialization //
+///////////////////
+
+template<typename BasicJsonType>
+class serializer
+{
+ using string_t = typename BasicJsonType::string_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ public:
+ /*!
+ @param[in] s output stream to serialize to
+ @param[in] ichar indentation character to use
+ */
+ serializer(output_adapter_t<char> s, const char ichar)
+ : o(std::move(s)), loc(std::localeconv()),
+ thousands_sep(loc->thousands_sep == nullptr ? '\0' : * (loc->thousands_sep)),
+ decimal_point(loc->decimal_point == nullptr ? '\0' : * (loc->decimal_point)),
+ indent_char(ichar), indent_string(512, indent_char) {}
+
+ // delete because of pointer members
+ serializer(const serializer&) = delete;
+ serializer& operator=(const serializer&) = delete;
+
+ /*!
+ @brief internal implementation of the serialization function
+
+ This function is called by the public member function dump and organizes
+ the serialization internally. The indentation level is propagated as
+ additional parameter. In case of arrays and objects, the function is
+ called recursively.
+
+ - strings and object keys are escaped using `escape_string()`
+ - integer numbers are converted implicitly via `operator<<`
+ - floating-point numbers are converted to a string using `"%g"` format
+
+ @param[in] val value to serialize
+ @param[in] pretty_print whether the output shall be pretty-printed
+ @param[in] indent_step the indent level
+ @param[in] current_indent the current indent level (only used internally)
+ */
+ void dump(const BasicJsonType& val, const bool pretty_print,
+ const bool ensure_ascii,
+ const unsigned int indent_step,
+ const unsigned int current_indent = 0)
+ {
+ switch (val.m_type)
+ {
+ case value_t::object:
+ {
+ if (val.m_value.object->empty())
+ {
+ o->write_characters("{}", 2);
+ return;
+ }
+
+ if (pretty_print)
+ {
+ o->write_characters("{\n", 2);
+
+ // variable to hold indentation for recursive calls
+ const auto new_indent = current_indent + indent_step;
+ if (JSON_UNLIKELY(indent_string.size() < new_indent))
+ {
+ indent_string.resize(indent_string.size() * 2, ' ');
+ }
+
+ // first n-1 elements
+ auto i = val.m_value.object->cbegin();
+ for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
+ {
+ o->write_characters(indent_string.c_str(), new_indent);
+ o->write_character('\"');
+ dump_escaped(i->first, ensure_ascii);
+ o->write_characters("\": ", 3);
+ dump(i->second, true, ensure_ascii, indent_step, new_indent);
+ o->write_characters(",\n", 2);
+ }
+
+ // last element
+ assert(i != val.m_value.object->cend());
+ assert(std::next(i) == val.m_value.object->cend());
+ o->write_characters(indent_string.c_str(), new_indent);
+ o->write_character('\"');
+ dump_escaped(i->first, ensure_ascii);
+ o->write_characters("\": ", 3);
+ dump(i->second, true, ensure_ascii, indent_step, new_indent);
+
+ o->write_character('\n');
+ o->write_characters(indent_string.c_str(), current_indent);
+ o->write_character('}');
+ }
+ else
+ {
+ o->write_character('{');
+
+ // first n-1 elements
+ auto i = val.m_value.object->cbegin();
+ for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
+ {
+ o->write_character('\"');
+ dump_escaped(i->first, ensure_ascii);
+ o->write_characters("\":", 2);
+ dump(i->second, false, ensure_ascii, indent_step, current_indent);
+ o->write_character(',');
+ }
+
+ // last element
+ assert(i != val.m_value.object->cend());
+ assert(std::next(i) == val.m_value.object->cend());
+ o->write_character('\"');
+ dump_escaped(i->first, ensure_ascii);
+ o->write_characters("\":", 2);
+ dump(i->second, false, ensure_ascii, indent_step, current_indent);
+
+ o->write_character('}');
+ }
+
+ return;
+ }
+
+ case value_t::array:
+ {
+ if (val.m_value.array->empty())
+ {
+ o->write_characters("[]", 2);
+ return;
+ }
+
+ if (pretty_print)
+ {
+ o->write_characters("[\n", 2);
+
+ // variable to hold indentation for recursive calls
+ const auto new_indent = current_indent + indent_step;
+ if (JSON_UNLIKELY(indent_string.size() < new_indent))
+ {
+ indent_string.resize(indent_string.size() * 2, ' ');
+ }
+
+ // first n-1 elements
+ for (auto i = val.m_value.array->cbegin();
+ i != val.m_value.array->cend() - 1; ++i)
+ {
+ o->write_characters(indent_string.c_str(), new_indent);
+ dump(*i, true, ensure_ascii, indent_step, new_indent);
+ o->write_characters(",\n", 2);
+ }
+
+ // last element
+ assert(not val.m_value.array->empty());
+ o->write_characters(indent_string.c_str(), new_indent);
+ dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);
+
+ o->write_character('\n');
+ o->write_characters(indent_string.c_str(), current_indent);
+ o->write_character(']');
+ }
+ else
+ {
+ o->write_character('[');
+
+ // first n-1 elements
+ for (auto i = val.m_value.array->cbegin();
+ i != val.m_value.array->cend() - 1; ++i)
+ {
+ dump(*i, false, ensure_ascii, indent_step, current_indent);
+ o->write_character(',');
+ }
+
+ // last element
+ assert(not val.m_value.array->empty());
+ dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
+
+ o->write_character(']');
+ }
+
+ return;
+ }
+
+ case value_t::string:
+ {
+ o->write_character('\"');
+ dump_escaped(*val.m_value.string, ensure_ascii);
+ o->write_character('\"');
+ return;
+ }
+
+ case value_t::boolean:
+ {
+ if (val.m_value.boolean)
+ {
+ o->write_characters("true", 4);
+ }
+ else
+ {
+ o->write_characters("false", 5);
+ }
+ return;
+ }
+
+ case value_t::number_integer:
+ {
+ dump_integer(val.m_value.number_integer);
+ return;
+ }
+
+ case value_t::number_unsigned:
+ {
+ dump_integer(val.m_value.number_unsigned);
+ return;
+ }
+
+ case value_t::number_float:
+ {
+ dump_float(val.m_value.number_float);
+ return;
+ }
+
+ case value_t::discarded:
+ {
+ o->write_characters("<discarded>", 11);
+ return;
+ }
+
+ case value_t::null:
+ {
+ o->write_characters("null", 4);
+ return;
+ }
+ }
+ }
+
+ private:
+ /*!
+ @brief returns the number of expected bytes following in UTF-8 string
+
+ @param[in] u the first byte of a UTF-8 string
+ @return the number of expected bytes following
+ */
+ static constexpr std::size_t bytes_following(const uint8_t u)
+ {
+ return ((u <= 127) ? 0
+ : ((192 <= u and u <= 223) ? 1
+ : ((224 <= u and u <= 239) ? 2
+ : ((240 <= u and u <= 247) ? 3 : std::string::npos))));
+ }
+
+ /*!
+ @brief calculates the extra space to escape a JSON string
+
+ @param[in] s the string to escape
+ @param[in] ensure_ascii whether to escape non-ASCII characters with
+ \uXXXX sequences
+ @return the number of characters required to escape string @a s
+
+ @complexity Linear in the length of string @a s.
+ */
+ static std::size_t extra_space(const string_t& s,
+ const bool ensure_ascii) noexcept
+ {
+ std::size_t res = 0;
+
+ for (std::size_t i = 0; i < s.size(); ++i)
+ {
+ switch (s[i])
+ {
+ // control characters that can be escaped with a backslash
+ case '"':
+ case '\\':
+ case '\b':
+ case '\f':
+ case '\n':
+ case '\r':
+ case '\t':
+ {
+ // from c (1 byte) to \x (2 bytes)
+ res += 1;
+ break;
+ }
+
+ // control characters that need \uxxxx escaping
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x0B:
+ case 0x0E:
+ case 0x0F:
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ case 0x18:
+ case 0x19:
+ case 0x1A:
+ case 0x1B:
+ case 0x1C:
+ case 0x1D:
+ case 0x1E:
+ case 0x1F:
+ {
+ // from c (1 byte) to \uxxxx (6 bytes)
+ res += 5;
+ break;
+ }
+
+ default:
+ {
+ if (ensure_ascii and (s[i] & 0x80 or s[i] == 0x7F))
+ {
+ const auto bytes = bytes_following(static_cast<uint8_t>(s[i]));
+ // invalid characters will be detected by throw_if_invalid_utf8
+ assert (bytes != std::string::npos);
+
+ if (bytes == 3)
+ {
+ // codepoints that need 4 bytes (i.e., 3 additional
+ // bytes) in UTF-8 need a surrogate pair when \u
+ // escaping is used: from 4 bytes to \uxxxx\uxxxx
+ // (12 bytes)
+ res += (12 - bytes - 1);
+ }
+ else
+ {
+ // from x bytes to \uxxxx (6 bytes)
+ res += (6 - bytes - 1);
+ }
+
+ // skip the additional bytes
+ i += bytes;
+ }
+ break;
+ }
+ }
+ }
+
+ return res;
+ }
+
+ static void escape_codepoint(int codepoint, string_t& result, std::size_t& pos)
+ {
+ // expecting a proper codepoint
+ assert(0x00 <= codepoint and codepoint <= 0x10FFFF);
+
+ // the last written character was the backslash before the 'u'
+ assert(result[pos] == '\\');
+
+ // write the 'u'
+ result[++pos] = 'u';
+
+ // convert a number 0..15 to its hex representation (0..f)
+ static const std::array<char, 16> hexify =
+ {
+ {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+ }
+ };
+
+ if (codepoint < 0x10000)
+ {
+ // codepoints U+0000..U+FFFF can be represented as \uxxxx.
+ result[++pos] = hexify[(codepoint >> 12) & 0x0F];
+ result[++pos] = hexify[(codepoint >> 8) & 0x0F];
+ result[++pos] = hexify[(codepoint >> 4) & 0x0F];
+ result[++pos] = hexify[codepoint & 0x0F];
+ }
+ else
+ {
+ // codepoints U+10000..U+10FFFF need a surrogate pair to be
+ // represented as \uxxxx\uxxxx.
+ // http://www.unicode.org/faq/utf_bom.html#utf16-4
+ codepoint -= 0x10000;
+ const int high_surrogate = 0xD800 | ((codepoint >> 10) & 0x3FF);
+ const int low_surrogate = 0xDC00 | (codepoint & 0x3FF);
+ result[++pos] = hexify[(high_surrogate >> 12) & 0x0F];
+ result[++pos] = hexify[(high_surrogate >> 8) & 0x0F];
+ result[++pos] = hexify[(high_surrogate >> 4) & 0x0F];
+ result[++pos] = hexify[high_surrogate & 0x0F];
+ ++pos; // backslash is already in output
+ result[++pos] = 'u';
+ result[++pos] = hexify[(low_surrogate >> 12) & 0x0F];
+ result[++pos] = hexify[(low_surrogate >> 8) & 0x0F];
+ result[++pos] = hexify[(low_surrogate >> 4) & 0x0F];
+ result[++pos] = hexify[low_surrogate & 0x0F];
+ }
+
+ ++pos;
+ }
+
+ /*!
+ @brief dump escaped string
+
+ Escape a string by replacing certain special characters by a sequence of an
+ escape character (backslash) and another character and other control
+ characters by a sequence of "\u" followed by a four-digit hex
+ representation. The escaped string is written to output stream @a o.
+
+ @param[in] s the string to escape
+ @param[in] ensure_ascii whether to escape non-ASCII characters with
+ \uXXXX sequences
+
+ @complexity Linear in the length of string @a s.
+ */
+ void dump_escaped(const string_t& s, const bool ensure_ascii) const
+ {
+ throw_if_invalid_utf8(s);
+
+ const auto space = extra_space(s, ensure_ascii);
+ if (space == 0)
+ {
+ o->write_characters(s.c_str(), s.size());
+ return;
+ }
+
+ // create a result string of necessary size
+ string_t result(s.size() + space, '\\');
+ std::size_t pos = 0;
+
+ for (std::size_t i = 0; i < s.size(); ++i)
+ {
+ switch (s[i])
+ {
+ case '"': // quotation mark (0x22)
+ {
+ result[pos + 1] = '"';
+ pos += 2;
+ break;
+ }
+
+ case '\\': // reverse solidus (0x5C)
+ {
+ // nothing to change
+ pos += 2;
+ break;
+ }
+
+ case '\b': // backspace (0x08)
+ {
+ result[pos + 1] = 'b';
+ pos += 2;
+ break;
+ }
+
+ case '\f': // formfeed (0x0C)
+ {
+ result[pos + 1] = 'f';
+ pos += 2;
+ break;
+ }
+
+ case '\n': // newline (0x0A)
+ {
+ result[pos + 1] = 'n';
+ pos += 2;
+ break;
+ }
+
+ case '\r': // carriage return (0x0D)
+ {
+ result[pos + 1] = 'r';
+ pos += 2;
+ break;
+ }
+
+ case '\t': // horizontal tab (0x09)
+ {
+ result[pos + 1] = 't';
+ pos += 2;
+ break;
+ }
+
+ default:
+ {
+ // escape control characters (0x00..0x1F) or, if
+ // ensure_ascii parameter is used, non-ASCII characters
+ if ((0x00 <= s[i] and s[i] <= 0x1F) or
+ (ensure_ascii and (s[i] & 0x80 or s[i] == 0x7F)))
+ {
+ const auto bytes = bytes_following(static_cast<uint8_t>(s[i]));
+ // invalid characters will be detected by throw_if_invalid_utf8
+ assert (bytes != std::string::npos);
+
+ // check that the additional bytes are present
+ assert(i + bytes < s.size());
+
+ // to use \uxxxx escaping, we first need to calculate
+ // the codepoint from the UTF-8 bytes
+ int codepoint = 0;
+
+ // bytes is unsigned type:
+ assert(bytes <= 3);
+ switch (bytes)
+ {
+ case 0:
+ {
+ codepoint = s[i] & 0xFF;
+ break;
+ }
+
+ case 1:
+ {
+ codepoint = ((s[i] & 0x3F) << 6)
+ + (s[i + 1] & 0x7F);
+ break;
+ }
+
+ case 2:
+ {
+ codepoint = ((s[i] & 0x1F) << 12)
+ + ((s[i + 1] & 0x7F) << 6)
+ + (s[i + 2] & 0x7F);
+ break;
+ }
+
+ case 3:
+ {
+ codepoint = ((s[i] & 0xF) << 18)
+ + ((s[i + 1] & 0x7F) << 12)
+ + ((s[i + 2] & 0x7F) << 6)
+ + (s[i + 3] & 0x7F);
+ break;
+ }
+
+ default:
+ break; // LCOV_EXCL_LINE
+ }
+
+ escape_codepoint(codepoint, result, pos);
+ i += bytes;
+ }
+ else
+ {
+ // all other characters are added as-is
+ result[pos++] = s[i];
+ }
+ break;
+ }
+ }
+ }
+
+ assert(pos == result.size());
+ o->write_characters(result.c_str(), result.size());
+ }
+
+ /*!
+ @brief dump an integer
+
+ Dump a given integer to output stream @a o. Works internally with
+ @a number_buffer.
+
+ @param[in] x integer number (signed or unsigned) to dump
+ @tparam NumberType either @a number_integer_t or @a number_unsigned_t
+ */
+ template<typename NumberType, detail::enable_if_t<
+ std::is_same<NumberType, number_unsigned_t>::value or
+ std::is_same<NumberType, number_integer_t>::value,
+ int> = 0>
+ void dump_integer(NumberType x)
+ {
+ // special case for "0"
+ if (x == 0)
+ {
+ o->write_character('0');
+ return;
+ }
+
+ const bool is_negative = (x <= 0) and (x != 0); // see issue #755
+ std::size_t i = 0;
+
+ while (x != 0)
+ {
+ // spare 1 byte for '\0'
+ assert(i < number_buffer.size() - 1);
+
+ const auto digit = std::labs(static_cast<long>(x % 10));
+ number_buffer[i++] = static_cast<char>('0' + digit);
+ x /= 10;
+ }
+
+ if (is_negative)
+ {
+ // make sure there is capacity for the '-'
+ assert(i < number_buffer.size() - 2);
+ number_buffer[i++] = '-';
+ }
+
+ std::reverse(number_buffer.begin(), number_buffer.begin() + i);
+ o->write_characters(number_buffer.data(), i);
+ }
+
+ /*!
+ @brief dump a floating-point number
+
+ Dump a given floating-point number to output stream @a o. Works internally
+ with @a number_buffer.
+
+ @param[in] x floating-point number to dump
+ */
+ void dump_float(number_float_t x)
+ {
+ // NaN / inf
+ if (not std::isfinite(x) or std::isnan(x))
+ {
+ o->write_characters("null", 4);
+ return;
+ }
+
+ // get number of digits for a text -> float -> text round-trip
+ static constexpr auto d = std::numeric_limits<number_float_t>::digits10;
+
+ // the actual conversion
+ std::ptrdiff_t len = snprintf(number_buffer.data(), number_buffer.size(), "%.*g", d, x);
+
+ // negative value indicates an error
+ assert(len > 0);
+ // check if buffer was large enough
+ assert(static_cast<std::size_t>(len) < number_buffer.size());
+
+ // erase thousands separator
+ if (thousands_sep != '\0')
+ {
+ const auto end = std::remove(number_buffer.begin(),
+ number_buffer.begin() + len, thousands_sep);
+ std::fill(end, number_buffer.end(), '\0');
+ assert((end - number_buffer.begin()) <= len);
+ len = (end - number_buffer.begin());
+ }
+
+ // convert decimal point to '.'
+ if (decimal_point != '\0' and decimal_point != '.')
+ {
+ const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
+ if (dec_pos != number_buffer.end())
+ {
+ *dec_pos = '.';
+ }
+ }
+
+ o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));
+
+ // determine if need to append ".0"
+ const bool value_is_int_like =
+ std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,
+ [](char c)
+ {
+ return (c == '.' or c == 'e');
+ });
+
+ if (value_is_int_like)
+ {
+ o->write_characters(".0", 2);
+ }
+ }
+
+ /*!
+ @brief check whether a string is UTF-8 encoded
+
+ The function checks each byte of a string whether it is UTF-8 encoded. The
+ result of the check is stored in the @a state parameter. The function must
+ be called initially with state 0 (accept). State 1 means the string must
+ be rejected, because the current byte is not allowed. If the string is
+ completely processed, but the state is non-zero, the string ended
+ prematurely; that is, the last byte indicated more bytes should have
+ followed.
+
+ @param[in,out] state the state of the decoding
+ @param[in] byte next byte to decode
+
+ @note The function has been edited: a std::array is used and the code
+ point is not calculated.
+
+ @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+ @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
+ */
+ static void decode(uint8_t& state, const uint8_t byte)
+ {
+ static const std::array<uint8_t, 400> utf8d =
+ {
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF
+ 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF
+ 0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF
+ 0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF
+ 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
+ 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
+ 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
+ 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8
+ }
+ };
+
+ const uint8_t type = utf8d[byte];
+ state = utf8d[256u + state * 16u + type];
+ }
+
+ /*!
+ @brief throw an exception if a string is not UTF-8 encoded
+
+ @param[in] str UTF-8 string to check
+ @throw type_error.316 if passed string is not UTF-8 encoded
+
+ @since version 3.0.0
+ */
+ static void throw_if_invalid_utf8(const std::string& str)
+ {
+ // start with state 0 (= accept)
+ uint8_t state = 0;
+
+ for (size_t i = 0; i < str.size(); ++i)
+ {
+ const auto byte = static_cast<uint8_t>(str[i]);
+ decode(state, byte);
+ if (state == 1)
+ {
+ // state 1 means reject
+ std::stringstream ss;
+ ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << static_cast<int>(byte);
+ JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + ss.str()));
+ }
+ }
+
+ if (state != 0)
+ {
+ // we finish reading, but do not accept: string was incomplete
+ std::stringstream ss;
+ ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << static_cast<int>(static_cast<uint8_t>(str.back()));
+ JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + ss.str()));
+ }
+ }
+
+ private:
+ /// the output of the serializer
+ output_adapter_t<char> o = nullptr;
+
+ /// a (hopefully) large enough character buffer
+ std::array<char, 64> number_buffer{{}};
+
+ /// the locale
+ const std::lconv* loc = nullptr;
+ /// the locale's thousand separator character
+ const char thousands_sep = '\0';
+ /// the locale's decimal point character
+ const char decimal_point = '\0';
+
+ /// the indentation character
+ const char indent_char;
+
+ /// the indentation string
+ string_t indent_string;
+};
+
+template<typename BasicJsonType>
+class json_ref
+{
+ public:
+ using value_type = BasicJsonType;
+
+ json_ref(value_type&& value)
+ : owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true)
+ {}
+
+ json_ref(const value_type& value)
+ : value_ref(const_cast<value_type*>(&value)), is_rvalue(false)
+ {}
+
+ json_ref(std::initializer_list<json_ref> init)
+ : owned_value(init), value_ref(&owned_value), is_rvalue(true)
+ {}
+
+ template<class... Args>
+ json_ref(Args&& ... args)
+ : owned_value(std::forward<Args>(args)...), value_ref(&owned_value), is_rvalue(true)
+ {}
+
+ // class should be movable only
+ json_ref(json_ref&&) = default;
+ json_ref(const json_ref&) = delete;
+ json_ref& operator=(const json_ref&) = delete;
+
+ value_type moved_or_copied() const
+ {
+ if (is_rvalue)
+ {
+ return std::move(*value_ref);
+ }
+ return *value_ref;
+ }
+
+ value_type const& operator*() const
+ {
+ return *static_cast<value_type const*>(value_ref);
+ }
+
+ value_type const* operator->() const
+ {
+ return static_cast<value_type const*>(value_ref);
+ }
+
+ private:
+ mutable value_type owned_value = nullptr;
+ value_type* value_ref = nullptr;
+ const bool is_rvalue;
};
+} // namespace detail
+
+/// namespace to hold default `to_json` / `from_json` functions
+namespace
+{
+constexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value;
+constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value;
}
+
+/*!
+@brief default JSONSerializer template argument
+
+This serializer ignores the template arguments and uses ADL
+([argument-dependent lookup](http://en.cppreference.com/w/cpp/language/adl))
+for serialization.
+*/
+template<typename, typename>
+struct adl_serializer
+{
+ /*!
+ @brief convert a JSON value to any value type
+
+ This function is usually called by the `get()` function of the
+ @ref basic_json class (either explicit or via conversion operators).
+
+ @param[in] j JSON value to read from
+ @param[in,out] val value to write to
+ */
+ template<typename BasicJsonType, typename ValueType>
+ static void from_json(BasicJsonType&& j, ValueType& val) noexcept(
+ noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))
+ {
+ ::nlohmann::from_json(std::forward<BasicJsonType>(j), val);
+ }
+
+ /*!
+ @brief convert any value type to a JSON value
+
+ This function is usually called by the constructors of the @ref basic_json
+ class.
+
+ @param[in,out] j JSON value to write to
+ @param[in] val value to read from
+ */
+ template<typename BasicJsonType, typename ValueType>
+ static void to_json(BasicJsonType& j, ValueType&& val) noexcept(
+ noexcept(::nlohmann::to_json(j, std::forward<ValueType>(val))))
+ {
+ ::nlohmann::to_json(j, std::forward<ValueType>(val));
+ }
+};
+
+/*!
+@brief JSON Pointer
+
+A JSON pointer defines a string syntax for identifying a specific value
+within a JSON document. It can be used with functions `at` and
+`operator[]`. Furthermore, JSON pointers are the base for JSON patches.
+
+@sa [RFC 6901](https://tools.ietf.org/html/rfc6901)
+
+@since version 2.0.0
+*/
+class json_pointer
+{
+ /// allow basic_json to access private members
+ NLOHMANN_BASIC_JSON_TPL_DECLARATION
+ friend class basic_json;
+
+ public:
+ /*!
+ @brief create JSON pointer
+
+ Create a JSON pointer according to the syntax described in
+ [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3).
+
+ @param[in] s string representing the JSON pointer; if omitted, the empty
+ string is assumed which references the whole JSON value
+
+ @throw parse_error.107 if the given JSON pointer @a s is nonempty and
+ does not begin with a slash (`/`); see example below
+
+ @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s
+ is not followed by `0` (representing `~`) or `1` (representing `/`);
+ see example below
+
+ @liveexample{The example shows the construction several valid JSON
+ pointers as well as the exceptional behavior.,json_pointer}
+
+ @since version 2.0.0
+ */
+ explicit json_pointer(const std::string& s = "") : reference_tokens(split(s)) {}
+
+ /*!
+ @brief return a string representation of the JSON pointer
+
+ @invariant For each JSON pointer `ptr`, it holds:
+ @code {.cpp}
+ ptr == json_pointer(ptr.to_string());
+ @endcode
+
+ @return a string representation of the JSON pointer
+
+ @liveexample{The example shows the result of `to_string`.,
+ json_pointer__to_string}
+
+ @since version 2.0.0
+ */
+ std::string to_string() const noexcept
+ {
+ return std::accumulate(reference_tokens.begin(), reference_tokens.end(),
+ std::string{},
+ [](const std::string & a, const std::string & b)
+ {
+ return a + "/" + escape(b);
+ });
+ }
+
+ /// @copydoc to_string()
+ operator std::string() const
+ {
+ return to_string();
+ }
+
+ /*!
+ @param[in] s reference token to be converted into an array index
+
+ @return integer representation of @a s
+
+ @throw out_of_range.404 if string @a s could not be converted to an integer
+ */
+ static int array_index(const std::string& s)
+ {
+ size_t processed_chars = 0;
+ const int res = std::stoi(s, &processed_chars);
+
+ // check if the string was completely read
+ if (JSON_UNLIKELY(processed_chars != s.size()))
+ {
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'"));
+ }
+
+ return res;
+ }
+
+ private:
+ /*!
+ @brief remove and return last reference pointer
+ @throw out_of_range.405 if JSON pointer has no parent
+ */
+ std::string pop_back()
+ {
+ if (JSON_UNLIKELY(is_root()))
+ {
+ JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
+ }
+
+ auto last = reference_tokens.back();
+ reference_tokens.pop_back();
+ return last;
+ }
+
+ /// return whether pointer points to the root document
+ bool is_root() const
+ {
+ return reference_tokens.empty();
+ }
+
+ json_pointer top() const
+ {
+ if (JSON_UNLIKELY(is_root()))
+ {
+ JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
+ }
+
+ json_pointer result = *this;
+ result.reference_tokens = {reference_tokens[0]};
+ return result;
+ }
+
+ /*!
+ @brief create and return a reference to the pointed to value
+
+ @complexity Linear in the number of reference tokens.
+
+ @throw parse_error.109 if array index is not a number
+ @throw type_error.313 if value cannot be unflattened
+ */
+ NLOHMANN_BASIC_JSON_TPL_DECLARATION
+ NLOHMANN_BASIC_JSON_TPL& get_and_create(NLOHMANN_BASIC_JSON_TPL& j) const;
+
+ /*!
+ @brief return a reference to the pointed to value
+
+ @note This version does not throw if a value is not present, but tries to
+ create nested values instead. For instance, calling this function
+ with pointer `"/this/that"` on a null value is equivalent to calling
+ `operator[]("this").operator[]("that")` on that value, effectively
+ changing the null value to an object.
+
+ @param[in] ptr a JSON value
+
+ @return reference to the JSON value pointed to by the JSON pointer
+
+ @complexity Linear in the length of the JSON pointer.
+
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index was not a number
+ @throw out_of_range.404 if the JSON pointer can not be resolved
+ */
+ NLOHMANN_BASIC_JSON_TPL_DECLARATION
+ NLOHMANN_BASIC_JSON_TPL& get_unchecked(NLOHMANN_BASIC_JSON_TPL* ptr) const;
+
+ /*!
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index was not a number
+ @throw out_of_range.402 if the array index '-' is used
+ @throw out_of_range.404 if the JSON pointer can not be resolved
+ */
+ NLOHMANN_BASIC_JSON_TPL_DECLARATION
+ NLOHMANN_BASIC_JSON_TPL& get_checked(NLOHMANN_BASIC_JSON_TPL* ptr) const;
+
+ /*!
+ @brief return a const reference to the pointed to value
+
+ @param[in] ptr a JSON value
+
+ @return const reference to the JSON value pointed to by the JSON
+ pointer
+
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index was not a number
+ @throw out_of_range.402 if the array index '-' is used
+ @throw out_of_range.404 if the JSON pointer can not be resolved
+ */
+ NLOHMANN_BASIC_JSON_TPL_DECLARATION
+ const NLOHMANN_BASIC_JSON_TPL& get_unchecked(const NLOHMANN_BASIC_JSON_TPL* ptr) const;
+
+ /*!
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index was not a number
+ @throw out_of_range.402 if the array index '-' is used
+ @throw out_of_range.404 if the JSON pointer can not be resolved
+ */
+ NLOHMANN_BASIC_JSON_TPL_DECLARATION
+ const NLOHMANN_BASIC_JSON_TPL& get_checked(const NLOHMANN_BASIC_JSON_TPL* ptr) const;
+
+ /*!
+ @brief split the string input to reference tokens
+
+ @note This function is only called by the json_pointer constructor.
+ All exceptions below are documented there.
+
+ @throw parse_error.107 if the pointer is not empty or begins with '/'
+ @throw parse_error.108 if character '~' is not followed by '0' or '1'
+ */
+ static std::vector<std::string> split(const std::string& reference_string)
+ {
+ std::vector<std::string> result;
+
+ // special case: empty reference string -> no reference tokens
+ if (reference_string.empty())
+ {
+ return result;
+ }
+
+ // check if nonempty reference string begins with slash
+ if (JSON_UNLIKELY(reference_string[0] != '/'))
+ {
+ JSON_THROW(detail::parse_error::create(107, 1,
+ "JSON pointer must be empty or begin with '/' - was: '" +
+ reference_string + "'"));
+ }
+
+ // extract the reference tokens:
+ // - slash: position of the last read slash (or end of string)
+ // - start: position after the previous slash
+ for (
+ // search for the first slash after the first character
+ std::size_t slash = reference_string.find_first_of('/', 1),
+ // set the beginning of the first reference token
+ start = 1;
+ // we can stop if start == string::npos+1 = 0
+ start != 0;
+ // set the beginning of the next reference token
+ // (will eventually be 0 if slash == std::string::npos)
+ start = slash + 1,
+ // find next slash
+ slash = reference_string.find_first_of('/', start))
+ {
+ // use the text between the beginning of the reference token
+ // (start) and the last slash (slash).
+ auto reference_token = reference_string.substr(start, slash - start);
+
+ // check reference tokens are properly escaped
+ for (std::size_t pos = reference_token.find_first_of('~');
+ pos != std::string::npos;
+ pos = reference_token.find_first_of('~', pos + 1))
+ {
+ assert(reference_token[pos] == '~');
+
+ // ~ must be followed by 0 or 1
+ if (JSON_UNLIKELY(pos == reference_token.size() - 1 or
+ (reference_token[pos + 1] != '0' and
+ reference_token[pos + 1] != '1')))
+ {
+ JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'"));
+ }
+ }
+
+ // finally, store the reference token
+ unescape(reference_token);
+ result.push_back(reference_token);
+ }
+
+ return result;
+ }
+
+ /*!
+ @brief replace all occurrences of a substring by another string
+
+ @param[in,out] s the string to manipulate; changed so that all
+ occurrences of @a f are replaced with @a t
+ @param[in] f the substring to replace with @a t
+ @param[in] t the string to replace @a f
+
+ @pre The search string @a f must not be empty. **This precondition is
+ enforced with an assertion.**
+
+ @since version 2.0.0
+ */
+ static void replace_substring(std::string& s, const std::string& f,
+ const std::string& t)
+ {
+ assert(not f.empty());
+ for (auto pos = s.find(f); // find first occurrence of f
+ pos != std::string::npos; // make sure f was found
+ s.replace(pos, f.size(), t), // replace with t, and
+ pos = s.find(f, pos + t.size())) // find next occurrence of f
+ {}
+ }
+
+ /// escape "~"" to "~0" and "/" to "~1"
+ static std::string escape(std::string s)
+ {
+ replace_substring(s, "~", "~0");
+ replace_substring(s, "/", "~1");
+ return s;
+ }
+
+ /// unescape "~1" to tilde and "~0" to slash (order is important!)
+ static void unescape(std::string& s)
+ {
+ replace_substring(s, "~1", "/");
+ replace_substring(s, "~0", "~");
+ }
+
+ /*!
+ @param[in] reference_string the reference string to the current value
+ @param[in] value the value to consider
+ @param[in,out] result the result object to insert values to
+
+ @note Empty objects or arrays are flattened to `null`.
+ */
+ NLOHMANN_BASIC_JSON_TPL_DECLARATION
+ static void flatten(const std::string& reference_string,
+ const NLOHMANN_BASIC_JSON_TPL& value,
+ NLOHMANN_BASIC_JSON_TPL& result);
+
+ /*!
+ @param[in] value flattened JSON
+
+ @return unflattened JSON
+
+ @throw parse_error.109 if array index is not a number
+ @throw type_error.314 if value is not an object
+ @throw type_error.315 if object values are not primitive
+ @throw type_error.313 if value cannot be unflattened
+ */
+ NLOHMANN_BASIC_JSON_TPL_DECLARATION
+ static NLOHMANN_BASIC_JSON_TPL
+ unflatten(const NLOHMANN_BASIC_JSON_TPL& value);
+
+ friend bool operator==(json_pointer const& lhs,
+ json_pointer const& rhs) noexcept;
+
+ friend bool operator!=(json_pointer const& lhs,
+ json_pointer const& rhs) noexcept;
+
+ /// the reference tokens
+ std::vector<std::string> reference_tokens;
+};
+
/*!
@brief a class to store JSON values
@@ -149,11 +7300,14 @@ default; will be used in @ref number_integer_t)
default; will be used in @ref number_float_t)
@tparam AllocatorType type of the allocator to use (`std::allocator` by
default)
+@tparam JSONSerializer the serializer to resolve internal calls to `to_json()`
+and `from_json()` (@ref adl_serializer by default)
@requirement The class satisfies the following concept requirements:
- Basic
- [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible):
- JSON values can be default constructed. The result will be a JSON null value.
+ JSON values can be default constructed. The result will be a JSON null
+ value.
- [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible):
A JSON value can be constructed from an rvalue argument.
- [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible):
@@ -168,8 +7322,8 @@ default)
- [StandardLayoutType](http://en.cppreference.com/w/cpp/concept/StandardLayoutType):
JSON values have
[standard layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout):
- All non-static data members are private and standard layout types, the class
- has no virtual functions or (virtual) base classes.
+ All non-static data members are private and standard layout types, the
+ class has no virtual functions or (virtual) base classes.
- Library-wide
- [EqualityComparable](http://en.cppreference.com/w/cpp/concept/EqualityComparable):
JSON values can be compared with `==`, see @ref
@@ -208,29 +7362,77 @@ Format](http://rfc7159.net/rfc7159)
@nosubgrouping
*/
-template <
- template<typename U, typename V, typename... Args> class ObjectType = std::map,
- template<typename U, typename... Args> class ArrayType = std::vector,
- class StringType = std::string,
- class BooleanType = bool,
- class NumberIntegerType = std::int64_t,
- class NumberUnsignedType = std::uint64_t,
- class NumberFloatType = double,
- template<typename U> class AllocatorType = std::allocator
- >
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
class basic_json
{
private:
+ template<detail::value_t> friend struct detail::external_constructor;
+ friend ::nlohmann::json_pointer;
+ friend ::nlohmann::detail::parser<basic_json>;
+ friend ::nlohmann::detail::serializer<basic_json>;
+ template<typename BasicJsonType>
+ friend class ::nlohmann::detail::iter_impl;
+ template<typename BasicJsonType, typename CharType>
+ friend class ::nlohmann::detail::binary_writer;
+ template<typename BasicJsonType>
+ friend class ::nlohmann::detail::binary_reader;
+
/// workaround type for MSVC
- using basic_json_t = basic_json<ObjectType, ArrayType, StringType,
- BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType,
- AllocatorType>;
+ using basic_json_t = NLOHMANN_BASIC_JSON_TPL;
+
+ // convenience aliases for types residing in namespace detail;
+ using lexer = ::nlohmann::detail::lexer<basic_json>;
+ using parser = ::nlohmann::detail::parser<basic_json>;
+
+ using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t;
+ template<typename BasicJsonType>
+ using internal_iterator = ::nlohmann::detail::internal_iterator<BasicJsonType>;
+ template<typename BasicJsonType>
+ using iter_impl = ::nlohmann::detail::iter_impl<BasicJsonType>;
+ template<typename Iterator>
+ using iteration_proxy = ::nlohmann::detail::iteration_proxy<Iterator>;
+ template<typename Base> using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator<Base>;
+
+ template<typename CharType>
+ using output_adapter_t = ::nlohmann::detail::output_adapter_t<CharType>;
+
+ using binary_reader = ::nlohmann::detail::binary_reader<basic_json>;
+ template<typename CharType> using binary_writer = ::nlohmann::detail::binary_writer<basic_json, CharType>;
+
+ using serializer = ::nlohmann::detail::serializer<basic_json>;
public:
- // forward declarations
- template<typename U> class iter_impl;
- template<typename Base> class json_reverse_iterator;
- class json_pointer;
+ using value_t = detail::value_t;
+ /// @copydoc nlohmann::json_pointer
+ using json_pointer = ::nlohmann::json_pointer;
+ template<typename T, typename SFINAE>
+ using json_serializer = JSONSerializer<T, SFINAE>;
+ /// helper type for initializer lists of basic_json values
+ using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
+
+ ////////////////
+ // exceptions //
+ ////////////////
+
+ /// @name exceptions
+ /// Classes to implement user-defined exceptions.
+ /// @{
+
+ /// @copydoc detail::exception
+ using exception = detail::exception;
+ /// @copydoc detail::parse_error
+ using parse_error = detail::parse_error;
+ /// @copydoc detail::invalid_iterator
+ using invalid_iterator = detail::invalid_iterator;
+ /// @copydoc detail::type_error
+ using type_error = detail::type_error;
+ /// @copydoc detail::out_of_range
+ using out_of_range = detail::out_of_range;
+ /// @copydoc detail::other_error
+ using other_error = detail::other_error;
+
+ /// @}
+
/////////////////////
// container types //
@@ -282,6 +7484,84 @@ class basic_json
return allocator_type();
}
+ /*!
+ @brief returns version information on the library
+
+ This function returns a JSON object with information about the library,
+ including the version number and information on the platform and compiler.
+
+ @return JSON object holding version information
+ key | description
+ ----------- | ---------------
+ `compiler` | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version).
+ `copyright` | The copyright line for the library as string.
+ `name` | The name of the library as string.
+ `platform` | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`.
+ `url` | The URL of the project as string.
+ `version` | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string).
+
+ @liveexample{The following code shows an example output of the `meta()`
+ function.,meta}
+
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes to any JSON value.
+
+ @complexity Constant.
+
+ @since 2.1.0
+ */
+ static basic_json meta()
+ {
+ basic_json result;
+
+ result["copyright"] = "(C) 2013-2017 Niels Lohmann";
+ result["name"] = "JSON for Modern C++";
+ result["url"] = "https://github.com/nlohmann/json";
+ result["version"] =
+ {
+ {"string", "3.0.1"}, {"major", 3}, {"minor", 0}, {"patch", 1}
+ };
+
+#ifdef _WIN32
+ result["platform"] = "win32";
+#elif defined __linux__
+ result["platform"] = "linux";
+#elif defined __APPLE__
+ result["platform"] = "apple";
+#elif defined __unix__
+ result["platform"] = "unix";
+#else
+ result["platform"] = "unknown";
+#endif
+
+#if defined(__ICC) || defined(__INTEL_COMPILER)
+ result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}};
+#elif defined(__clang__)
+ result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}};
+#elif defined(__GNUC__) || defined(__GNUG__)
+ result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}};
+#elif defined(__HP_cc) || defined(__HP_aCC)
+ result["compiler"] = "hp"
+#elif defined(__IBMCPP__)
+ result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}};
+#elif defined(_MSC_VER)
+ result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}};
+#elif defined(__PGI)
+ result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}};
+#elif defined(__SUNPRO_CC)
+ result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}};
+#else
+ result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}};
+#endif
+
+#ifdef __cplusplus
+ result["compiler"]["c++"] = std::to_string(__cplusplus);
+#else
+ result["compiler"]["c++"] = "unknown";
+#endif
+ return result;
+ }
+
///////////////////////////
// JSON value data types //
@@ -292,6 +7572,14 @@ class basic_json
/// the template arguments passed to class @ref basic_json.
/// @{
+#if defined(JSON_HAS_CPP_14)
+ // Use transparent comparator if possible, combined with perfect forwarding
+ // on find() and count() calls prevents unnecessary string construction.
+ using object_comparator_t = std::less<>;
+#else
+ using object_comparator_t = std::less<StringType>;
+#endif
+
/*!
@brief a type for an object
@@ -352,7 +7640,7 @@ class basic_json
[RFC 7159](http://rfc7159.net/rfc7159) specifies:
> An implementation may set limits on the maximum depth of nesting.
- In this class, the object's limit of nesting is not constraint explicitly.
+ In this class, the object's limit of nesting is not explicitly constrained.
However, a maximum depth of nesting may be introduced by the compiler or
runtime environment. A theoretical limit can be queried by calling the
@ref max_size function of a JSON object.
@@ -377,7 +7665,7 @@ class basic_json
*/
using object_t = ObjectType<StringType,
basic_json,
- std::less<StringType>,
+ object_comparator_t,
AllocatorType<std::pair<const StringType,
basic_json>>>;
@@ -411,7 +7699,7 @@ class basic_json
[RFC 7159](http://rfc7159.net/rfc7159) specifies:
> An implementation may set limits on the maximum depth of nesting.
- In this class, the array's limit of nesting is not constraint explicitly.
+ In this class, the array's limit of nesting is not explicitly constrained.
However, a maximum depth of nesting may be introduced by the compiler or
runtime environment. A theoretical limit can be queried by calling the
@ref max_size function of a JSON array.
@@ -449,6 +7737,12 @@ class basic_json
std::string
@endcode
+ #### Encoding
+
+ Strings are stored in UTF-8 encoding. Therefore, functions like
+ `std::string::size()` or `std::string::length()` return the number of
+ bytes in the string rather than the number of characters or glyphs.
+
#### String comparison
[RFC 7159](http://rfc7159.net/rfc7159) states:
@@ -713,47 +8007,6 @@ class basic_json
/// @}
-
- ///////////////////////////
- // JSON type enumeration //
- ///////////////////////////
-
- /*!
- @brief the JSON type enumeration
-
- This enumeration collects the different JSON types. It is internally used
- to distinguish the stored values, and the functions @ref is_null(), @ref
- is_object(), @ref is_array(), @ref is_string(), @ref is_boolean(), @ref
- is_number() (with @ref is_number_integer(), @ref is_number_unsigned(), and
- @ref is_number_float()), @ref is_discarded(), @ref is_primitive(), and
- @ref is_structured() rely on it.
-
- @note There are three enumeration entries (number_integer,
- number_unsigned, and number_float), because the library distinguishes
- these three types for numbers: @ref number_unsigned_t is used for unsigned
- integers, @ref number_integer_t is used for signed integers, and @ref
- number_float_t is used for floating-point numbers or to approximate
- integers which do not fit in the limits of their respective type.
-
- @sa @ref basic_json(const value_t value_type) -- create a JSON value with
- the default value for a given type
-
- @since version 1.0.0
- */
- enum class value_t : uint8_t
- {
- null, ///< null value
- object, ///< object (unordered set of name/value pairs)
- array, ///< array (ordered collection of values)
- string, ///< string value
- boolean, ///< boolean value
- number_integer, ///< number value (signed integer)
- number_unsigned, ///< number value (unsigned integer)
- number_float, ///< number value (floating-point)
- discarded ///< discarded by the the parser callback function
- };
-
-
private:
/// helper for exception-safe object creation
@@ -761,13 +8014,15 @@ class basic_json
static T* create(Args&& ... args)
{
AllocatorType<T> alloc;
+ using AllocatorTraits = std::allocator_traits<AllocatorType<T>>;
+
auto deleter = [&](T * object)
{
- alloc.deallocate(object, 1);
+ AllocatorTraits::deallocate(alloc, object, 1);
};
- std::unique_ptr<T, decltype(deleter)> object(alloc.allocate(1), deleter);
- alloc.construct(object.get(), std::forward<Args>(args)...);
- assert(object.get() != nullptr);
+ std::unique_ptr<T, decltype(deleter)> object(AllocatorTraits::allocate(alloc, 1), deleter);
+ AllocatorTraits::construct(alloc, object.get(), std::forward<Args>(args)...);
+ assert(object != nullptr);
return object.release();
}
@@ -875,14 +8130,16 @@ class basic_json
case value_t::null:
{
+ object = nullptr; // silence warning, see #821
break;
}
default:
{
- if (t == value_t::null)
+ object = nullptr; // silence warning, see #821
+ if (JSON_UNLIKELY(t == value_t::null))
{
- throw std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21 2.0.10"); // LCOV_EXCL_LINE
+ JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.0.1")); // LCOV_EXCL_LINE
}
break;
}
@@ -895,17 +8152,70 @@ class basic_json
string = create<string_t>(value);
}
+ /// constructor for rvalue strings
+ json_value(string_t&& value)
+ {
+ string = create<string_t>(std::move(value));
+ }
+
/// constructor for objects
json_value(const object_t& value)
{
object = create<object_t>(value);
}
+ /// constructor for rvalue objects
+ json_value(object_t&& value)
+ {
+ object = create<object_t>(std::move(value));
+ }
+
/// constructor for arrays
json_value(const array_t& value)
{
array = create<array_t>(value);
}
+
+ /// constructor for rvalue arrays
+ json_value(array_t&& value)
+ {
+ array = create<array_t>(std::move(value));
+ }
+
+ void destroy(value_t t)
+ {
+ switch (t)
+ {
+ case value_t::object:
+ {
+ AllocatorType<object_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, object);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, object, 1);
+ break;
+ }
+
+ case value_t::array:
+ {
+ AllocatorType<array_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, array);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, array, 1);
+ break;
+ }
+
+ case value_t::string:
+ {
+ AllocatorType<string_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, string);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, string, 1);
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
};
/*!
@@ -930,42 +8240,31 @@ class basic_json
//////////////////////////
/*!
- @brief JSON callback events
+ @brief parser event types
- This enumeration lists the parser events that can trigger calling a
- callback function of type @ref parser_callback_t during parsing.
+ The parser callback distinguishes the following events:
+ - `object_start`: the parser read `{` and started to process a JSON object
+ - `key`: the parser read a key of a value in an object
+ - `object_end`: the parser read `}` and finished processing a JSON object
+ - `array_start`: the parser read `[` and started to process a JSON array
+ - `array_end`: the parser read `]` and finished processing a JSON array
+ - `value`: the parser finished reading a JSON value
@image html callback_events.png "Example when certain parse events are triggered"
- @since version 1.0.0
+ @sa @ref parser_callback_t for more information and examples
*/
- enum class parse_event_t : uint8_t
- {
- /// the parser read `{` and started to process a JSON object
- object_start,
- /// the parser read `}` and finished processing a JSON object
- object_end,
- /// the parser read `[` and started to process a JSON array
- array_start,
- /// the parser read `]` and finished processing a JSON array
- array_end,
- /// the parser read a key of a value in an object
- key,
- /// the parser finished reading a JSON value
- value
- };
+ using parse_event_t = typename parser::parse_event_t;
/*!
@brief per-element parser callback type
With a parser callback function, the result of parsing a JSON text can be
- influenced. When passed to @ref parse(std::istream&, const
- parser_callback_t) or @ref parse(const CharT, const parser_callback_t),
- it is called on certain events (passed as @ref parse_event_t via parameter
- @a event) with a set recursion depth @a depth and context JSON value
- @a parsed. The return value of the callback function is a boolean
- indicating whether the element that emitted the callback shall be kept or
- not.
+ influenced. When passed to @ref parse, it is called on certain events
+ (passed as @ref parse_event_t via parameter @a event) with a set recursion
+ depth @a depth and context JSON value @a parsed. The return value of the
+ callback function is a boolean indicating whether the element that emitted
+ the callback shall be kept or not.
We distinguish six scenarios (determined by the event type) in which the
callback function can be called. The following table describes the values
@@ -1002,14 +8301,11 @@ class basic_json
should be kept (`true`) or not (`false`). In the latter case, it is either
skipped completely or replaced by an empty discarded object.
- @sa @ref parse(std::istream&, parser_callback_t) or
- @ref parse(const CharT, const parser_callback_t) for examples
+ @sa @ref parse for examples
@since version 1.0.0
*/
- using parser_callback_t = std::function<bool(int depth,
- parse_event_t event,
- basic_json& parsed)>;
+ using parser_callback_t = typename parser::parser_callback_t;
//////////////////
@@ -1036,32 +8332,22 @@ class basic_json
object | `{}`
array | `[]`
- @param[in] value_type the type of the value to create
+ @param[in] v the type of the value to create
@complexity Constant.
- @throw std::bad_alloc if allocation for object, array, or string value
- fails
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes to any JSON value.
@liveexample{The following code shows the constructor for different @ref
value_t values,basic_json__value_t}
- @sa @ref basic_json(std::nullptr_t) -- create a `null` value
- @sa @ref basic_json(boolean_t value) -- create a boolean value
- @sa @ref basic_json(const string_t&) -- create a string value
- @sa @ref basic_json(const object_t&) -- create a object value
- @sa @ref basic_json(const array_t&) -- create a array value
- @sa @ref basic_json(const number_float_t) -- create a number
- (floating-point) value
- @sa @ref basic_json(const number_integer_t) -- create a number (integer)
- value
- @sa @ref basic_json(const number_unsigned_t) -- create a number (unsigned)
- value
+ @sa @ref clear() -- restores the postcondition of this constructor
@since version 1.0.0
*/
- basic_json(const value_t value_type)
- : m_type(value_type), m_value(value_type)
+ basic_json(const value_t v)
+ : m_type(v), m_value(v)
{
assert_invariant();
}
@@ -1091,473 +8377,72 @@ class basic_json
}
/*!
- @brief create an object (explicit)
-
- Create an object JSON value with a given content.
-
- @param[in] val a value for the object
-
- @complexity Linear in the size of the passed @a val.
-
- @throw std::bad_alloc if allocation for object value fails
-
- @liveexample{The following code shows the constructor with an @ref
- object_t parameter.,basic_json__object_t}
-
- @sa @ref basic_json(const CompatibleObjectType&) -- create an object value
- from a compatible STL container
-
- @since version 1.0.0
- */
- basic_json(const object_t& val)
- : m_type(value_t::object), m_value(val)
- {
- assert_invariant();
- }
-
- /*!
- @brief create an object (implicit)
-
- Create an object JSON value with a given content. This constructor allows
- any type @a CompatibleObjectType that can be used to construct values of
- type @ref object_t.
-
- @tparam CompatibleObjectType An object type whose `key_type` and
- `value_type` is compatible to @ref object_t. Examples include `std::map`,
- `std::unordered_map`, `std::multimap`, and `std::unordered_multimap` with
- a `key_type` of `std::string`, and a `value_type` from which a @ref
- basic_json value can be constructed.
-
- @param[in] val a value for the object
-
- @complexity Linear in the size of the passed @a val.
-
- @throw std::bad_alloc if allocation for object value fails
-
- @liveexample{The following code shows the constructor with several
- compatible object type parameters.,basic_json__CompatibleObjectType}
-
- @sa @ref basic_json(const object_t&) -- create an object value
-
- @since version 1.0.0
- */
- template<class CompatibleObjectType, typename std::enable_if<
- std::is_constructible<typename object_t::key_type, typename CompatibleObjectType::key_type>::value and
- std::is_constructible<basic_json, typename CompatibleObjectType::mapped_type>::value, int>::type = 0>
- basic_json(const CompatibleObjectType& val)
- : m_type(value_t::object)
- {
- using std::begin;
- using std::end;
- m_value.object = create<object_t>(begin(val), end(val));
- assert_invariant();
- }
-
- /*!
- @brief create an array (explicit)
-
- Create an array JSON value with a given content.
-
- @param[in] val a value for the array
-
- @complexity Linear in the size of the passed @a val.
-
- @throw std::bad_alloc if allocation for array value fails
-
- @liveexample{The following code shows the constructor with an @ref array_t
- parameter.,basic_json__array_t}
-
- @sa @ref basic_json(const CompatibleArrayType&) -- create an array value
- from a compatible STL containers
-
- @since version 1.0.0
- */
- basic_json(const array_t& val)
- : m_type(value_t::array), m_value(val)
- {
- assert_invariant();
- }
-
- /*!
- @brief create an array (implicit)
-
- Create an array JSON value with a given content. This constructor allows
- any type @a CompatibleArrayType that can be used to construct values of
- type @ref array_t.
-
- @tparam CompatibleArrayType An object type whose `value_type` is
- compatible to @ref array_t. Examples include `std::vector`, `std::deque`,
- `std::list`, `std::forward_list`, `std::array`, `std::set`,
- `std::unordered_set`, `std::multiset`, and `unordered_multiset` with a
- `value_type` from which a @ref basic_json value can be constructed.
-
- @param[in] val a value for the array
-
- @complexity Linear in the size of the passed @a val.
-
- @throw std::bad_alloc if allocation for array value fails
+ @brief create a JSON value
+
+ This is a "catch all" constructor for all compatible JSON types; that is,
+ types for which a `to_json()` method exists. The constructor forwards the
+ parameter @a val to that method (to `json_serializer<U>::to_json` method
+ with `U = uncvref_t<CompatibleType>`, to be exact).
+
+ Template type @a CompatibleType includes, but is not limited to, the
+ following types:
+ - **arrays**: @ref array_t and all kinds of compatible containers such as
+ `std::vector`, `std::deque`, `std::list`, `std::forward_list`,
+ `std::array`, `std::valarray`, `std::set`, `std::unordered_set`,
+ `std::multiset`, and `std::unordered_multiset` with a `value_type` from
+ which a @ref basic_json value can be constructed.
+ - **objects**: @ref object_t and all kinds of compatible associative
+ containers such as `std::map`, `std::unordered_map`, `std::multimap`,
+ and `std::unordered_multimap` with a `key_type` compatible to
+ @ref string_t and a `value_type` from which a @ref basic_json value can
+ be constructed.
+ - **strings**: @ref string_t, string literals, and all compatible string
+ containers can be used.
+ - **numbers**: @ref number_integer_t, @ref number_unsigned_t,
+ @ref number_float_t, and all convertible number types such as `int`,
+ `size_t`, `int64_t`, `float` or `double` can be used.
+ - **boolean**: @ref boolean_t / `bool` can be used.
+
+ See the examples below.
+
+ @tparam CompatibleType a type such that:
+ - @a CompatibleType is not derived from `std::istream`,
+ - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move
+ constructors),
+ - @a CompatibleType is not a @ref basic_json nested type (e.g.,
+ @ref json_pointer, @ref iterator, etc ...)
+ - @ref @ref json_serializer<U> has a
+ `to_json(basic_json_t&, CompatibleType&&)` method
+
+ @tparam U = `uncvref_t<CompatibleType>`
+
+ @param[in] val the value to be forwarded to the respective constructor
+
+ @complexity Usually linear in the size of the passed @a val, also
+ depending on the implementation of the called `to_json()`
+ method.
+
+ @exceptionsafety Depends on the called constructor. For types directly
+ supported by the library (i.e., all types for which no `to_json()` function
+ was provided), strong guarantee holds: if an exception is thrown, there are
+ no changes to any JSON value.
@liveexample{The following code shows the constructor with several
- compatible array type parameters.,basic_json__CompatibleArrayType}
-
- @sa @ref basic_json(const array_t&) -- create an array value
+ compatible types.,basic_json__CompatibleType}
- @since version 1.0.0
+ @since version 2.1.0
*/
- template<class CompatibleArrayType, typename std::enable_if<
- not std::is_same<CompatibleArrayType, typename basic_json_t::iterator>::value and
- not std::is_same<CompatibleArrayType, typename basic_json_t::const_iterator>::value and
- not std::is_same<CompatibleArrayType, typename basic_json_t::reverse_iterator>::value and
- not std::is_same<CompatibleArrayType, typename basic_json_t::const_reverse_iterator>::value and
- not std::is_same<CompatibleArrayType, typename array_t::iterator>::value and
- not std::is_same<CompatibleArrayType, typename array_t::const_iterator>::value and
- std::is_constructible<basic_json, typename CompatibleArrayType::value_type>::value, int>::type = 0>
- basic_json(const CompatibleArrayType& val)
- : m_type(value_t::array)
- {
- using std::begin;
- using std::end;
- m_value.array = create<array_t>(begin(val), end(val));
- assert_invariant();
- }
-
- /*!
- @brief create a string (explicit)
-
- Create an string JSON value with a given content.
-
- @param[in] val a value for the string
-
- @complexity Linear in the size of the passed @a val.
-
- @throw std::bad_alloc if allocation for string value fails
-
- @liveexample{The following code shows the constructor with an @ref
- string_t parameter.,basic_json__string_t}
-
- @sa @ref basic_json(const typename string_t::value_type*) -- create a
- string value from a character pointer
- @sa @ref basic_json(const CompatibleStringType&) -- create a string value
- from a compatible string container
-
- @since version 1.0.0
- */
- basic_json(const string_t& val)
- : m_type(value_t::string), m_value(val)
- {
- assert_invariant();
- }
-
- /*!
- @brief create a string (explicit)
-
- Create a string JSON value with a given content.
-
- @param[in] val a literal value for the string
-
- @complexity Linear in the size of the passed @a val.
-
- @throw std::bad_alloc if allocation for string value fails
-
- @liveexample{The following code shows the constructor with string literal
- parameter.,basic_json__string_t_value_type}
-
- @sa @ref basic_json(const string_t&) -- create a string value
- @sa @ref basic_json(const CompatibleStringType&) -- create a string value
- from a compatible string container
-
- @since version 1.0.0
- */
- basic_json(const typename string_t::value_type* val)
- : basic_json(string_t(val))
- {
- assert_invariant();
- }
-
- /*!
- @brief create a string (implicit)
-
- Create a string JSON value with a given content.
-
- @param[in] val a value for the string
-
- @tparam CompatibleStringType an string type which is compatible to @ref
- string_t, for instance `std::string`.
-
- @complexity Linear in the size of the passed @a val.
-
- @throw std::bad_alloc if allocation for string value fails
-
- @liveexample{The following code shows the construction of a string value
- from a compatible type.,basic_json__CompatibleStringType}
-
- @sa @ref basic_json(const string_t&) -- create a string value
- @sa @ref basic_json(const typename string_t::value_type*) -- create a
- string value from a character pointer
-
- @since version 1.0.0
- */
- template<class CompatibleStringType, typename std::enable_if<
- std::is_constructible<string_t, CompatibleStringType>::value, int>::type = 0>
- basic_json(const CompatibleStringType& val)
- : basic_json(string_t(val))
- {
- assert_invariant();
- }
-
- /*!
- @brief create a boolean (explicit)
-
- Creates a JSON boolean type from a given value.
-
- @param[in] val a boolean value to store
-
- @complexity Constant.
-
- @liveexample{The example below demonstrates boolean
- values.,basic_json__boolean_t}
-
- @since version 1.0.0
- */
- basic_json(boolean_t val) noexcept
- : m_type(value_t::boolean), m_value(val)
- {
- assert_invariant();
- }
-
- /*!
- @brief create an integer number (explicit)
-
- Create an integer number JSON value with a given content.
-
- @tparam T A helper type to remove this function via SFINAE in case @ref
- number_integer_t is the same as `int`. In this case, this constructor
- would have the same signature as @ref basic_json(const int value). Note
- the helper type @a T is not visible in this constructor's interface.
-
- @param[in] val an integer to create a JSON number from
-
- @complexity Constant.
-
- @liveexample{The example below shows the construction of an integer
- number value.,basic_json__number_integer_t}
-
- @sa @ref basic_json(const int) -- create a number value (integer)
- @sa @ref basic_json(const CompatibleNumberIntegerType) -- create a number
- value (integer) from a compatible number type
-
- @since version 1.0.0
- */
- template<typename T, typename std::enable_if<
- not (std::is_same<T, int>::value) and
- std::is_same<T, number_integer_t>::value, int>::type = 0>
- basic_json(const number_integer_t val) noexcept
- : m_type(value_t::number_integer), m_value(val)
- {
- assert_invariant();
- }
-
- /*!
- @brief create an integer number from an enum type (explicit)
-
- Create an integer number JSON value with a given content.
-
- @param[in] val an integer to create a JSON number from
-
- @note This constructor allows to pass enums directly to a constructor. As
- C++ has no way of specifying the type of an anonymous enum explicitly, we
- can only rely on the fact that such values implicitly convert to int. As
- int may already be the same type of number_integer_t, we may need to
- switch off the constructor @ref basic_json(const number_integer_t).
-
- @complexity Constant.
-
- @liveexample{The example below shows the construction of an integer
- number value from an anonymous enum.,basic_json__const_int}
-
- @sa @ref basic_json(const number_integer_t) -- create a number value
- (integer)
- @sa @ref basic_json(const CompatibleNumberIntegerType) -- create a number
- value (integer) from a compatible number type
-
- @since version 1.0.0
- */
- basic_json(const int val) noexcept
- : m_type(value_t::number_integer),
- m_value(static_cast<number_integer_t>(val))
- {
- assert_invariant();
- }
-
- /*!
- @brief create an integer number (implicit)
-
- Create an integer number JSON value with a given content. This constructor
- allows any type @a CompatibleNumberIntegerType that can be used to
- construct values of type @ref number_integer_t.
-
- @tparam CompatibleNumberIntegerType An integer type which is compatible to
- @ref number_integer_t. Examples include the types `int`, `int32_t`,
- `long`, and `short`.
-
- @param[in] val an integer to create a JSON number from
-
- @complexity Constant.
-
- @liveexample{The example below shows the construction of several integer
- number values from compatible
- types.,basic_json__CompatibleIntegerNumberType}
-
- @sa @ref basic_json(const number_integer_t) -- create a number value
- (integer)
- @sa @ref basic_json(const int) -- create a number value (integer)
-
- @since version 1.0.0
- */
- template<typename CompatibleNumberIntegerType, typename std::enable_if<
- std::is_constructible<number_integer_t, CompatibleNumberIntegerType>::value and
- std::numeric_limits<CompatibleNumberIntegerType>::is_integer and
- std::numeric_limits<CompatibleNumberIntegerType>::is_signed,
- CompatibleNumberIntegerType>::type = 0>
- basic_json(const CompatibleNumberIntegerType val) noexcept
- : m_type(value_t::number_integer),
- m_value(static_cast<number_integer_t>(val))
- {
- assert_invariant();
- }
-
- /*!
- @brief create an unsigned integer number (explicit)
-
- Create an unsigned integer number JSON value with a given content.
-
- @tparam T helper type to compare number_unsigned_t and unsigned int (not
- visible in) the interface.
-
- @param[in] val an integer to create a JSON number from
-
- @complexity Constant.
-
- @sa @ref basic_json(const CompatibleNumberUnsignedType) -- create a number
- value (unsigned integer) from a compatible number type
-
- @since version 2.0.0
- */
- template<typename T, typename std::enable_if<
- not (std::is_same<T, int>::value) and
- std::is_same<T, number_unsigned_t>::value, int>::type = 0>
- basic_json(const number_unsigned_t val) noexcept
- : m_type(value_t::number_unsigned), m_value(val)
- {
- assert_invariant();
- }
-
- /*!
- @brief create an unsigned number (implicit)
-
- Create an unsigned number JSON value with a given content. This
- constructor allows any type @a CompatibleNumberUnsignedType that can be
- used to construct values of type @ref number_unsigned_t.
-
- @tparam CompatibleNumberUnsignedType An integer type which is compatible
- to @ref number_unsigned_t. Examples may include the types `unsigned int`,
- `uint32_t`, or `unsigned short`.
-
- @param[in] val an unsigned integer to create a JSON number from
-
- @complexity Constant.
-
- @sa @ref basic_json(const number_unsigned_t) -- create a number value
- (unsigned)
-
- @since version 2.0.0
- */
- template<typename CompatibleNumberUnsignedType, typename std::enable_if <
- std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and
- std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and
- not std::numeric_limits<CompatibleNumberUnsignedType>::is_signed,
- CompatibleNumberUnsignedType>::type = 0>
- basic_json(const CompatibleNumberUnsignedType val) noexcept
- : m_type(value_t::number_unsigned),
- m_value(static_cast<number_unsigned_t>(val))
- {
- assert_invariant();
- }
-
- /*!
- @brief create a floating-point number (explicit)
-
- Create a floating-point number JSON value with a given content.
-
- @param[in] val a floating-point value to create a JSON number from
-
- @note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6
- disallows NaN values:
- > Numeric values that cannot be represented in the grammar below (such as
- > Infinity and NaN) are not permitted.
- In case the parameter @a val is not a number, a JSON null value is created
- instead.
-
- @complexity Constant.
-
- @liveexample{The following example creates several floating-point
- values.,basic_json__number_float_t}
-
- @sa @ref basic_json(const CompatibleNumberFloatType) -- create a number
- value (floating-point) from a compatible number type
-
- @since version 1.0.0
- */
- basic_json(const number_float_t val) noexcept
- : m_type(value_t::number_float), m_value(val)
- {
- // replace infinity and NAN by null
- if (not std::isfinite(val))
- {
- m_type = value_t::null;
- m_value = json_value();
- }
-
- assert_invariant();
- }
-
- /*!
- @brief create an floating-point number (implicit)
-
- Create an floating-point number JSON value with a given content. This
- constructor allows any type @a CompatibleNumberFloatType that can be used
- to construct values of type @ref number_float_t.
-
- @tparam CompatibleNumberFloatType A floating-point type which is
- compatible to @ref number_float_t. Examples may include the types `float`
- or `double`.
-
- @param[in] val a floating-point to create a JSON number from
-
- @note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6
- disallows NaN values:
- > Numeric values that cannot be represented in the grammar below (such as
- > Infinity and NaN) are not permitted.
- In case the parameter @a val is not a number, a JSON null value is
- created instead.
-
- @complexity Constant.
-
- @liveexample{The example below shows the construction of several
- floating-point number values from compatible
- types.,basic_json__CompatibleNumberFloatType}
-
- @sa @ref basic_json(const number_float_t) -- create a number value
- (floating-point)
-
- @since version 1.0.0
- */
- template<typename CompatibleNumberFloatType, typename = typename std::enable_if<
- std::is_constructible<number_float_t, CompatibleNumberFloatType>::value and
- std::is_floating_point<CompatibleNumberFloatType>::value>::type>
- basic_json(const CompatibleNumberFloatType val) noexcept
- : basic_json(number_float_t(val))
+ template<typename CompatibleType, typename U = detail::uncvref_t<CompatibleType>,
+ detail::enable_if_t<not std::is_base_of<std::istream, U>::value and
+ not std::is_same<U, basic_json_t>::value and
+ not detail::is_basic_json_nested_type<
+ basic_json_t, U>::value and
+ detail::has_to_json<basic_json, U>::value,
+ int> = 0>
+ basic_json(CompatibleType && val) noexcept(noexcept(JSONSerializer<U>::to_json(
+ std::declval<basic_json_t&>(), std::forward<CompatibleType>(val))))
{
+ JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));
assert_invariant();
}
@@ -1580,7 +8465,7 @@ class basic_json
1. The empty initializer list is written as `{}` which is exactly an empty
JSON object.
- 2. C++ has now way of describing mapped types other than to list a list of
+ 2. C++ has no way of describing mapped types other than to list a list of
pairs. As JSON requires that keys must be of type string, rule 2 is the
weakest constraint one can pose on initializer lists to interpret them
as an object.
@@ -1590,10 +8475,10 @@ class basic_json
With the rules described above, the following JSON values cannot be
expressed by an initializer list:
- - the empty array (`[]`): use @ref array(std::initializer_list<basic_json>)
+ - the empty array (`[]`): use @ref array(initializer_list_t)
with an empty initializer list in this case
- arrays whose elements satisfy rule 2: use @ref
- array(std::initializer_list<basic_json>) with the same initializer list
+ array(initializer_list_t) with the same initializer list
in this case
@note When used without parentheses around an empty initializer list, @ref
@@ -1605,41 +8490,46 @@ class basic_json
@param[in] type_deduction internal parameter; when set to `true`, the type
of the JSON value is deducted from the initializer list @a init; when set
to `false`, the type provided via @a manual_type is forced. This mode is
- used by the functions @ref array(std::initializer_list<basic_json>) and
- @ref object(std::initializer_list<basic_json>).
+ used by the functions @ref array(initializer_list_t) and
+ @ref object(initializer_list_t).
@param[in] manual_type internal parameter; when @a type_deduction is set
to `false`, the created JSON value will use the provided type (only @ref
value_t::array and @ref value_t::object are valid); when @a type_deduction
is set to `true`, this parameter has no effect
- @throw std::domain_error if @a type_deduction is `false`, @a manual_type
- is `value_t::object`, but @a init contains an element which is not a pair
- whose first element is a string; example: `"cannot create object from
- initializer list"`
+ @throw type_error.301 if @a type_deduction is `false`, @a manual_type is
+ `value_t::object`, but @a init contains an element which is not a pair
+ whose first element is a string. In this case, the constructor could not
+ create an object. If @a type_deduction would have be `true`, an array
+ would have been created. See @ref object(initializer_list_t)
+ for an example.
@complexity Linear in the size of the initializer list @a init.
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes to any JSON value.
+
@liveexample{The example below shows how JSON values are created from
initializer lists.,basic_json__list_init_t}
- @sa @ref array(std::initializer_list<basic_json>) -- create a JSON array
+ @sa @ref array(initializer_list_t) -- create a JSON array
value from an initializer list
- @sa @ref object(std::initializer_list<basic_json>) -- create a JSON object
+ @sa @ref object(initializer_list_t) -- create a JSON object
value from an initializer list
@since version 1.0.0
*/
- basic_json(std::initializer_list<basic_json> init,
+ basic_json(initializer_list_t init,
bool type_deduction = true,
value_t manual_type = value_t::array)
{
// check if each element is an array with two elements whose first
// element is a string
bool is_an_object = std::all_of(init.begin(), init.end(),
- [](const basic_json & element)
+ [](const detail::json_ref<basic_json>& element_ref)
{
- return element.is_array() and element.size() == 2 and element[0].is_string();
+ return (element_ref->is_array() and element_ref->size() == 2 and (*element_ref)[0].is_string());
});
// adjust type if type deduction is not wanted
@@ -1652,9 +8542,9 @@ class basic_json
}
// if object is wanted but impossible, throw an exception
- if (manual_type == value_t::object and not is_an_object)
+ if (JSON_UNLIKELY(manual_type == value_t::object and not is_an_object))
{
- throw std::domain_error("cannot create object from initializer list");
+ JSON_THROW(type_error::create(301, "cannot create object from initializer list"));
}
}
@@ -1664,16 +8554,19 @@ class basic_json
m_type = value_t::object;
m_value = value_t::object;
- std::for_each(init.begin(), init.end(), [this](const basic_json & element)
+ std::for_each(init.begin(), init.end(), [this](const detail::json_ref<basic_json>& element_ref)
{
- m_value.object->emplace(*(element[0].m_value.string), element[1]);
+ auto element = element_ref.moved_or_copied();
+ m_value.object->emplace(
+ std::move(*((*element.m_value.array)[0].m_value.string)),
+ std::move((*element.m_value.array)[1]));
});
}
else
{
// the initializer list describes an array -> create array
m_type = value_t::array;
- m_value.array = create<array_t>(init);
+ m_value.array = create<array_t>(init.begin(), init.end());
}
assert_invariant();
@@ -1688,7 +8581,7 @@ class basic_json
@note This function is only needed to express two edge cases that cannot
be realized with the initializer list constructor (@ref
- basic_json(std::initializer_list<basic_json>, bool, value_t)). These cases
+ basic_json(initializer_list_t, bool, value_t)). These cases
are:
1. creating an array whose elements are all pairs whose first element is a
string -- in this case, the initializer list constructor would create an
@@ -1703,18 +8596,20 @@ class basic_json
@complexity Linear in the size of @a init.
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes to any JSON value.
+
@liveexample{The following code shows an example for the `array`
function.,array}
- @sa @ref basic_json(std::initializer_list<basic_json>, bool, value_t) --
+ @sa @ref basic_json(initializer_list_t, bool, value_t) --
create a JSON value from an initializer list
- @sa @ref object(std::initializer_list<basic_json>) -- create a JSON object
+ @sa @ref object(initializer_list_t) -- create a JSON object
value from an initializer list
@since version 1.0.0
*/
- static basic_json array(std::initializer_list<basic_json> init =
- std::initializer_list<basic_json>())
+ static basic_json array(initializer_list_t init = {})
{
return basic_json(init, false, value_t::array);
}
@@ -1727,34 +8622,37 @@ class basic_json
the initializer list is empty, the empty object `{}` is created.
@note This function is only added for symmetry reasons. In contrast to the
- related function @ref array(std::initializer_list<basic_json>), there are
+ related function @ref array(initializer_list_t), there are
no cases which can only be expressed by this function. That is, any
initializer list @a init can also be passed to the initializer list
- constructor @ref basic_json(std::initializer_list<basic_json>, bool,
- value_t).
+ constructor @ref basic_json(initializer_list_t, bool, value_t).
@param[in] init initializer list to create an object from (optional)
@return JSON object value
- @throw std::domain_error if @a init is not a pair whose first elements are
- strings; thrown by
- @ref basic_json(std::initializer_list<basic_json>, bool, value_t)
+ @throw type_error.301 if @a init is not a list of pairs whose first
+ elements are strings. In this case, no object can be created. When such a
+ value is passed to @ref basic_json(initializer_list_t, bool, value_t),
+ an array would have been created from the passed initializer list @a init.
+ See example below.
@complexity Linear in the size of @a init.
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes to any JSON value.
+
@liveexample{The following code shows an example for the `object`
function.,object}
- @sa @ref basic_json(std::initializer_list<basic_json>, bool, value_t) --
+ @sa @ref basic_json(initializer_list_t, bool, value_t) --
create a JSON value from an initializer list
- @sa @ref array(std::initializer_list<basic_json>) -- create a JSON array
+ @sa @ref array(initializer_list_t) -- create a JSON array
value from an initializer list
@since version 1.0.0
*/
- static basic_json object(std::initializer_list<basic_json> init =
- std::initializer_list<basic_json>())
+ static basic_json object(initializer_list_t init = {})
{
return basic_json(init, false, value_t::object);
}
@@ -1763,14 +8661,18 @@ class basic_json
@brief construct an array with count copies of given value
Constructs a JSON array value by creating @a cnt copies of a passed value.
- In case @a cnt is `0`, an empty array is created. As postcondition,
- `std::distance(begin(),end()) == cnt` holds.
+ In case @a cnt is `0`, an empty array is created.
@param[in] cnt the number of JSON copies of @a val to create
@param[in] val the JSON value to copy
+ @post `std::distance(begin(),end()) == cnt` holds.
+
@complexity Linear in @a cnt.
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes to any JSON value.
+
@liveexample{The following code shows examples for the @ref
basic_json(size_type\, const basic_json&)
constructor.,basic_json__size_type_basic_json}
@@ -1789,12 +8691,13 @@ class basic_json
Constructs the JSON value with the contents of the range `[first, last)`.
The semantics depends on the different types a JSON value can have:
- - In case of primitive types (number, boolean, or string), @a first must
- be `begin()` and @a last must be `end()`. In this case, the value is
- copied. Otherwise, std::out_of_range is thrown.
+ - In case of a null type, invalid_iterator.206 is thrown.
+ - In case of other primitive types (number, boolean, or string), @a first
+ must be `begin()` and @a last must be `end()`. In this case, the value is
+ copied. Otherwise, invalid_iterator.204 is thrown.
- In case of structured types (array, object), the constructor behaves as
- similar versions for `std::vector`.
- - In case of a null type, std::domain_error is thrown.
+ similar versions for `std::vector` or `std::map`; that is, a JSON array
+ or object is constructed from the values in the range.
@tparam InputIT an input iterator type (@ref iterator or @ref
const_iterator)
@@ -1803,19 +8706,36 @@ class basic_json
@param[in] last end of the range to copy from (excluded)
@pre Iterators @a first and @a last must be initialized. **This
- precondition is enforced with an assertion.**
-
- @throw std::domain_error if iterators are not compatible; that is, do not
- belong to the same JSON value; example: `"iterators are not compatible"`
- @throw std::out_of_range if iterators are for a primitive type (number,
- boolean, or string) where an out of range error can be detected easily;
- example: `"iterators out of range"`
- @throw std::bad_alloc if allocation for object, array, or string fails
- @throw std::domain_error if called with a null value; example: `"cannot
- use construct with iterators from null"`
+ precondition is enforced with an assertion (see warning).** If
+ assertions are switched off, a violation of this precondition yields
+ undefined behavior.
+
+ @pre Range `[first, last)` is valid. Usually, this precondition cannot be
+ checked efficiently. Only certain edge cases are detected; see the
+ description of the exceptions below. A violation of this precondition
+ yields undefined behavior.
+
+ @warning A precondition is enforced with a runtime assertion that will
+ result in calling `std::abort` if this precondition is not met.
+ Assertions can be disabled by defining `NDEBUG` at compile time.
+ See http://en.cppreference.com/w/cpp/error/assert for more
+ information.
+
+ @throw invalid_iterator.201 if iterators @a first and @a last are not
+ compatible (i.e., do not belong to the same JSON value). In this case,
+ the range `[first, last)` is undefined.
+ @throw invalid_iterator.204 if iterators @a first and @a last belong to a
+ primitive type (number, boolean, or string), but @a first does not point
+ to the first element any more. In this case, the range `[first, last)` is
+ undefined. See example code below.
+ @throw invalid_iterator.206 if iterators @a first and @a last belong to a
+ null value. In this case, the range `[first, last)` is undefined.
@complexity Linear in distance between @a first and @a last.
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes to any JSON value.
+
@liveexample{The example below shows several ways to create JSON values by
specifying a subrange with iterators.,basic_json__InputIt_InputIt}
@@ -1830,9 +8750,9 @@ class basic_json
assert(last.m_object != nullptr);
// make sure iterator fits the current value
- if (first.m_object != last.m_object)
+ if (JSON_UNLIKELY(first.m_object != last.m_object))
{
- throw std::domain_error("iterators are not compatible");
+ JSON_THROW(invalid_iterator::create(201, "iterators are not compatible"));
}
// copy type from first iterator
@@ -1847,17 +8767,16 @@ class basic_json
case value_t::number_unsigned:
case value_t::string:
{
- if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end())
+ if (JSON_UNLIKELY(not first.m_it.primitive_iterator.is_begin()
+ or not last.m_it.primitive_iterator.is_end()))
{
- throw std::out_of_range("iterators out of range");
+ JSON_THROW(invalid_iterator::create(204, "iterators out of range"));
}
break;
}
default:
- {
break;
- }
}
switch (m_type)
@@ -1894,64 +8813,36 @@ class basic_json
case value_t::object:
{
- m_value.object = create<object_t>(first.m_it.object_iterator, last.m_it.object_iterator);
+ m_value.object = create<object_t>(first.m_it.object_iterator,
+ last.m_it.object_iterator);
break;
}
case value_t::array:
{
- m_value.array = create<array_t>(first.m_it.array_iterator, last.m_it.array_iterator);
+ m_value.array = create<array_t>(first.m_it.array_iterator,
+ last.m_it.array_iterator);
break;
}
default:
- {
- throw std::domain_error("cannot use construct with iterators from " + first.m_object->type_name());
- }
+ JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " +
+ std::string(first.m_object->type_name())));
}
assert_invariant();
}
- /*!
- @brief construct a JSON value given an input stream
-
- @param[in,out] i stream to read a serialized JSON value from
- @param[in] cb a parser callback function of type @ref parser_callback_t
- which is used to control the deserialization by filtering unwanted values
- (optional)
-
- @complexity Linear in the length of the input. The parser is a predictive
- LL(1) parser. The complexity can be higher if the parser callback function
- @a cb has a super-linear complexity.
-
- @note A UTF-8 byte order mark is silently ignored.
-
- @deprecated This constructor is deprecated and will be removed in version
- 3.0.0 to unify the interface of the library. Deserialization will be
- done by stream operators or by calling one of the `parse` functions,
- e.g. @ref parse(std::istream&, const parser_callback_t). That is, calls
- like `json j(i);` for an input stream @a i need to be replaced by
- `json j = json::parse(i);`. See the example below.
-
- @liveexample{The example below demonstrates constructing a JSON value from
- a `std::stringstream` with and without callback
- function.,basic_json__istream}
-
- @since version 2.0.0, deprecated in version 2.0.3, to be removed in
- version 3.0.0
- */
- JSON_DEPRECATED
- explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr)
- {
- *this = parser(i, cb).parse();
- assert_invariant();
- }
///////////////////////////////////////
// other constructors and destructor //
///////////////////////////////////////
+ /// @private
+ basic_json(const detail::json_ref<basic_json>& ref)
+ : basic_json(ref.moved_or_copied())
+ {}
+
/*!
@brief copy constructor
@@ -1959,16 +8850,19 @@ class basic_json
@param[in] other the JSON value to copy
+ @post `*this == other`
+
@complexity Linear in the size of @a other.
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes to any JSON value.
+
@requirement This function helps `basic_json` satisfying the
[Container](http://en.cppreference.com/w/cpp/concept/Container)
requirements:
- The complexity is linear.
- As postcondition, it holds: `other == basic_json(other)`.
- @throw std::bad_alloc if allocation for object, array, or string fails.
-
@liveexample{The following code shows an example for the copy
constructor.,basic_json__basic_json}
@@ -2025,9 +8919,7 @@ class basic_json
}
default:
- {
break;
- }
}
assert_invariant();
@@ -2042,10 +8934,18 @@ class basic_json
@param[in,out] other value to move to this object
- @post @a other is a JSON null value
+ @post `*this` has the same value as @a other before the call.
+ @post @a other is a JSON null value.
@complexity Constant.
+ @exceptionsafety No-throw guarantee: this constructor never throws
+ exceptions.
+
+ @requirement This function helps `basic_json` satisfying the
+ [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible)
+ requirements.
+
@liveexample{The code below shows the move constructor explicitly called
via std::move.,basic_json__moveconstructor}
@@ -2070,7 +8970,7 @@ class basic_json
Copy assignment operator. Copies a JSON value via the "copy and swap"
strategy: It is expressed in terms of the copy constructor, destructor,
- and the swap() member function.
+ and the `swap()` member function.
@param[in] other value to copy from
@@ -2124,39 +9024,7 @@ class basic_json
~basic_json()
{
assert_invariant();
-
- switch (m_type)
- {
- case value_t::object:
- {
- AllocatorType<object_t> alloc;
- alloc.destroy(m_value.object);
- alloc.deallocate(m_value.object, 1);
- break;
- }
-
- case value_t::array:
- {
- AllocatorType<array_t> alloc;
- alloc.destroy(m_value.array);
- alloc.deallocate(m_value.array, 1);
- break;
- }
-
- case value_t::string:
- {
- AllocatorType<string_t> alloc;
- alloc.destroy(m_value.string);
- alloc.deallocate(m_value.string, 1);
- break;
- }
-
- default:
- {
- // all other types need no specific destructor
- break;
- }
- }
+ m_value.destroy(m_type);
}
/// @}
@@ -2175,46 +9043,53 @@ class basic_json
Serialization function for JSON values. The function tries to mimic
Python's `json.dumps()` function, and currently supports its @a indent
- parameter.
+ and @a ensure_ascii parameters.
@param[in] indent If indent is nonnegative, then array elements and object
members will be pretty-printed with that indent level. An indent level of
`0` will only insert newlines. `-1` (the default) selects the most compact
representation.
+ @param[in] indent_char The character to use for indentation if @a indent is
+ greater than `0`. The default is ` ` (space).
+ @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters
+ in the output are escaped with `\uXXXX` sequences, and the result consists
+ of ASCII characters only.
@return string containing the serialization of the JSON value
+ @throw type_error.316 if a string stored inside the JSON value is not
+ UTF-8 encoded
+
@complexity Linear.
- @liveexample{The following example shows the effect of different @a indent
- parameters to the result of the serialization.,dump}
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes in the JSON value.
+
+ @liveexample{The following example shows the effect of different @a indent\,
+ @a indent_char\, and @a ensure_ascii parameters to the result of the
+ serialization.,dump}
@see https://docs.python.org/2/library/json.html#json.dump
- @since version 1.0.0
+ @since version 1.0.0; indentation character @a indent_char, option
+ @a ensure_ascii and exceptions added in version 3.0.0
*/
- string_t dump(const int indent = -1) const
+ string_t dump(const int indent = -1, const char indent_char = ' ',
+ const bool ensure_ascii = false) const
{
- std::stringstream ss;
- // fix locale problems
- ss.imbue(std::locale::classic());
-
- // 6, 15 or 16 digits of precision allows round-trip IEEE 754
- // string->float->string, string->double->string or string->long
- // double->string; to be safe, we read this value from
- // std::numeric_limits<number_float_t>::digits10
- ss.precision(std::numeric_limits<double>::digits10);
+ string_t result;
+ serializer s(detail::output_adapter<char>(result), indent_char);
if (indent >= 0)
{
- dump(ss, true, static_cast<unsigned int>(indent));
+ s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));
}
else
{
- dump(ss, false, 0);
+ s.dump(*this, false, ensure_ascii, 0);
}
- return ss.str();
+ return result;
}
/*!
@@ -2224,6 +9099,17 @@ class basic_json
enumeration.
@return the type of the JSON value
+ Value type | return value
+ ------------------------- | -------------------------
+ null | value_t::null
+ boolean | value_t::boolean
+ string | value_t::string
+ number (integer) | value_t::number_integer
+ number (unsigned integer) | value_t::number_unsigned
+ number (floating-point) | value_t::number_float
+ object | value_t::object
+ array | value_t::array
+ discarded | value_t::discarded
@complexity Constant.
@@ -2233,6 +9119,9 @@ class basic_json
@liveexample{The following code exemplifies `type()` for all JSON
types.,type}
+ @sa @ref operator value_t() -- return the type of the JSON value (implicit)
+ @sa @ref type_name() -- return the type as string
+
@since version 1.0.0
*/
constexpr value_t type() const noexcept
@@ -2243,8 +9132,8 @@ class basic_json
/*!
@brief return whether type is primitive
- This function returns true iff the JSON type is primitive (string, number,
- boolean, or null).
+ This function returns true if and only if the JSON type is primitive
+ (string, number, boolean, or null).
@return `true` if type is primitive (string, number, boolean, or null),
`false` otherwise.
@@ -2273,8 +9162,8 @@ class basic_json
/*!
@brief return whether type is structured
- This function returns true iff the JSON type is structured (array or
- object).
+ This function returns true if and only if the JSON type is structured
+ (array or object).
@return `true` if type is structured (array or object), `false` otherwise.
@@ -2300,7 +9189,7 @@ class basic_json
/*!
@brief return whether value is null
- This function returns true iff the JSON value is null.
+ This function returns true if and only if the JSON value is null.
@return `true` if type is null, `false` otherwise.
@@ -2316,13 +9205,13 @@ class basic_json
*/
constexpr bool is_null() const noexcept
{
- return m_type == value_t::null;
+ return (m_type == value_t::null);
}
/*!
@brief return whether value is a boolean
- This function returns true iff the JSON value is a boolean.
+ This function returns true if and only if the JSON value is a boolean.
@return `true` if type is boolean, `false` otherwise.
@@ -2338,14 +9227,14 @@ class basic_json
*/
constexpr bool is_boolean() const noexcept
{
- return m_type == value_t::boolean;
+ return (m_type == value_t::boolean);
}
/*!
@brief return whether value is a number
- This function returns true iff the JSON value is a number. This includes
- both integer and floating-point values.
+ This function returns true if and only if the JSON value is a number. This
+ includes both integer (signed and unsigned) and floating-point values.
@return `true` if type is number (regardless whether integer, unsigned
integer or floating-type), `false` otherwise.
@@ -2374,8 +9263,8 @@ class basic_json
/*!
@brief return whether value is an integer number
- This function returns true iff the JSON value is an integer or unsigned
- integer number. This excludes floating-point values.
+ This function returns true if and only if the JSON value is a signed or
+ unsigned integer number. This excludes floating-point values.
@return `true` if type is an integer or unsigned integer number, `false`
otherwise.
@@ -2397,14 +9286,14 @@ class basic_json
*/
constexpr bool is_number_integer() const noexcept
{
- return m_type == value_t::number_integer or m_type == value_t::number_unsigned;
+ return (m_type == value_t::number_integer or m_type == value_t::number_unsigned);
}
/*!
@brief return whether value is an unsigned integer number
- This function returns true iff the JSON value is an unsigned integer
- number. This excludes floating-point and (signed) integer values.
+ This function returns true if and only if the JSON value is an unsigned
+ integer number. This excludes floating-point and signed integer values.
@return `true` if type is an unsigned integer number, `false` otherwise.
@@ -2425,14 +9314,14 @@ class basic_json
*/
constexpr bool is_number_unsigned() const noexcept
{
- return m_type == value_t::number_unsigned;
+ return (m_type == value_t::number_unsigned);
}
/*!
@brief return whether value is a floating-point number
- This function returns true iff the JSON value is a floating-point number.
- This excludes integer and unsigned integer values.
+ This function returns true if and only if the JSON value is a
+ floating-point number. This excludes signed and unsigned integer values.
@return `true` if type is a floating-point number, `false` otherwise.
@@ -2453,13 +9342,13 @@ class basic_json
*/
constexpr bool is_number_float() const noexcept
{
- return m_type == value_t::number_float;
+ return (m_type == value_t::number_float);
}
/*!
@brief return whether value is an object
- This function returns true iff the JSON value is an object.
+ This function returns true if and only if the JSON value is an object.
@return `true` if type is object, `false` otherwise.
@@ -2475,13 +9364,13 @@ class basic_json
*/
constexpr bool is_object() const noexcept
{
- return m_type == value_t::object;
+ return (m_type == value_t::object);
}
/*!
@brief return whether value is an array
- This function returns true iff the JSON value is an array.
+ This function returns true if and only if the JSON value is an array.
@return `true` if type is array, `false` otherwise.
@@ -2497,13 +9386,13 @@ class basic_json
*/
constexpr bool is_array() const noexcept
{
- return m_type == value_t::array;
+ return (m_type == value_t::array);
}
/*!
@brief return whether value is a string
- This function returns true iff the JSON value is a string.
+ This function returns true if and only if the JSON value is a string.
@return `true` if type is string, `false` otherwise.
@@ -2519,14 +9408,14 @@ class basic_json
*/
constexpr bool is_string() const noexcept
{
- return m_type == value_t::string;
+ return (m_type == value_t::string);
}
/*!
@brief return whether value is discarded
- This function returns true iff the JSON value was discarded during parsing
- with a callback function (see @ref parser_callback_t).
+ This function returns true if and only if the JSON value was discarded
+ during parsing with a callback function (see @ref parser_callback_t).
@note This function will always be `false` for JSON values after parsing.
That is, discarded values can only occur during parsing, but will be
@@ -2546,7 +9435,7 @@ class basic_json
*/
constexpr bool is_discarded() const noexcept
{
- return m_type == value_t::discarded;
+ return (m_type == value_t::discarded);
}
/*!
@@ -2565,6 +9454,9 @@ class basic_json
@liveexample{The following code exemplifies the @ref value_t operator for
all JSON types.,operator__value_t}
+ @sa @ref type() -- return the type of the JSON value (explicit)
+ @sa @ref type_name() -- return the type as string
+
@since version 1.0.0
*/
constexpr operator value_t() const noexcept
@@ -2579,244 +9471,97 @@ class basic_json
// value access //
//////////////////
- /// get an object (explicit)
- template<class T, typename std::enable_if<
- std::is_convertible<typename object_t::key_type, typename T::key_type>::value and
- std::is_convertible<basic_json_t, typename T::mapped_type>::value, int>::type = 0>
- T get_impl(T*) const
- {
- if (is_object())
- {
- return T(m_value.object->begin(), m_value.object->end());
- }
- else
- {
- throw std::domain_error("type must be object, but is " + type_name());
- }
- }
-
- /// get an object (explicit)
- object_t get_impl(object_t*) const
- {
- if (is_object())
- {
- return *(m_value.object);
- }
- else
- {
- throw std::domain_error("type must be object, but is " + type_name());
- }
- }
-
- /// get an array (explicit)
- template<class T, typename std::enable_if<
- std::is_convertible<basic_json_t, typename T::value_type>::value and
- not std::is_same<basic_json_t, typename T::value_type>::value and
- not std::is_arithmetic<T>::value and
- not std::is_convertible<std::string, T>::value and
- not has_mapped_type<T>::value, int>::type = 0>
- T get_impl(T*) const
- {
- if (is_array())
- {
- T to_vector;
- std::transform(m_value.array->begin(), m_value.array->end(),
- std::inserter(to_vector, to_vector.end()), [](basic_json i)
- {
- return i.get<typename T::value_type>();
- });
- return to_vector;
- }
- else
- {
- throw std::domain_error("type must be array, but is " + type_name());
- }
- }
-
- /// get an array (explicit)
- template<class T, typename std::enable_if<
- std::is_convertible<basic_json_t, T>::value and
- not std::is_same<basic_json_t, T>::value, int>::type = 0>
- std::vector<T> get_impl(std::vector<T>*) const
- {
- if (is_array())
- {
- std::vector<T> to_vector;
- to_vector.reserve(m_value.array->size());
- std::transform(m_value.array->begin(), m_value.array->end(),
- std::inserter(to_vector, to_vector.end()), [](basic_json i)
- {
- return i.get<T>();
- });
- return to_vector;
- }
- else
- {
- throw std::domain_error("type must be array, but is " + type_name());
- }
- }
-
- /// get an array (explicit)
- template<class T, typename std::enable_if<
- std::is_same<basic_json, typename T::value_type>::value and
- not has_mapped_type<T>::value, int>::type = 0>
- T get_impl(T*) const
- {
- if (is_array())
- {
- return T(m_value.array->begin(), m_value.array->end());
- }
- else
- {
- throw std::domain_error("type must be array, but is " + type_name());
- }
- }
-
- /// get an array (explicit)
- array_t get_impl(array_t*) const
- {
- if (is_array())
- {
- return *(m_value.array);
- }
- else
- {
- throw std::domain_error("type must be array, but is " + type_name());
- }
- }
-
- /// get a string (explicit)
- template<typename T, typename std::enable_if<
- std::is_convertible<string_t, T>::value, int>::type = 0>
- T get_impl(T*) const
- {
- if (is_string())
- {
- return *m_value.string;
- }
- else
- {
- throw std::domain_error("type must be string, but is " + type_name());
- }
- }
-
- /// get a number (explicit)
- template<typename T, typename std::enable_if<
- std::is_arithmetic<T>::value, int>::type = 0>
- T get_impl(T*) const
+ /// get a boolean (explicit)
+ boolean_t get_impl(boolean_t* /*unused*/) const
{
- switch (m_type)
+ if (JSON_LIKELY(is_boolean()))
{
- case value_t::number_integer:
- {
- return static_cast<T>(m_value.number_integer);
- }
-
- case value_t::number_unsigned:
- {
- return static_cast<T>(m_value.number_unsigned);
- }
-
- case value_t::number_float:
- {
- return static_cast<T>(m_value.number_float);
- }
-
- default:
- {
- throw std::domain_error("type must be number, but is " + type_name());
- }
+ return m_value.boolean;
}
- }
- /// get a boolean (explicit)
- constexpr boolean_t get_impl(boolean_t*) const
- {
- return is_boolean()
- ? m_value.boolean
- : throw std::domain_error("type must be boolean, but is " + type_name());
+ JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name())));
}
/// get a pointer to the value (object)
- object_t* get_impl_ptr(object_t*) noexcept
+ object_t* get_impl_ptr(object_t* /*unused*/) noexcept
{
return is_object() ? m_value.object : nullptr;
}
/// get a pointer to the value (object)
- constexpr const object_t* get_impl_ptr(const object_t*) const noexcept
+ constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept
{
return is_object() ? m_value.object : nullptr;
}
/// get a pointer to the value (array)
- array_t* get_impl_ptr(array_t*) noexcept
+ array_t* get_impl_ptr(array_t* /*unused*/) noexcept
{
return is_array() ? m_value.array : nullptr;
}
/// get a pointer to the value (array)
- constexpr const array_t* get_impl_ptr(const array_t*) const noexcept
+ constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept
{
return is_array() ? m_value.array : nullptr;
}
/// get a pointer to the value (string)
- string_t* get_impl_ptr(string_t*) noexcept
+ string_t* get_impl_ptr(string_t* /*unused*/) noexcept
{
return is_string() ? m_value.string : nullptr;
}
/// get a pointer to the value (string)
- constexpr const string_t* get_impl_ptr(const string_t*) const noexcept
+ constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept
{
return is_string() ? m_value.string : nullptr;
}
/// get a pointer to the value (boolean)
- boolean_t* get_impl_ptr(boolean_t*) noexcept
+ boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept
{
return is_boolean() ? &m_value.boolean : nullptr;
}
/// get a pointer to the value (boolean)
- constexpr const boolean_t* get_impl_ptr(const boolean_t*) const noexcept
+ constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept
{
return is_boolean() ? &m_value.boolean : nullptr;
}
/// get a pointer to the value (integer number)
- number_integer_t* get_impl_ptr(number_integer_t*) noexcept
+ number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept
{
return is_number_integer() ? &m_value.number_integer : nullptr;
}
/// get a pointer to the value (integer number)
- constexpr const number_integer_t* get_impl_ptr(const number_integer_t*) const noexcept
+ constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept
{
return is_number_integer() ? &m_value.number_integer : nullptr;
}
/// get a pointer to the value (unsigned number)
- number_unsigned_t* get_impl_ptr(number_unsigned_t*) noexcept
+ number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept
{
return is_number_unsigned() ? &m_value.number_unsigned : nullptr;
}
/// get a pointer to the value (unsigned number)
- constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t*) const noexcept
+ constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept
{
return is_number_unsigned() ? &m_value.number_unsigned : nullptr;
}
/// get a pointer to the value (floating-point number)
- number_float_t* get_impl_ptr(number_float_t*) noexcept
+ number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept
{
return is_number_float() ? &m_value.number_float : nullptr;
}
/// get a pointer to the value (floating-point number)
- constexpr const number_float_t* get_impl_ptr(const number_float_t*) const noexcept
+ constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept
{
return is_number_float() ? &m_value.number_float : nullptr;
}
@@ -2824,55 +9569,84 @@ class basic_json
/*!
@brief helper function to implement get_ref()
- This funcion helps to implement get_ref() without code duplication for
+ This function helps to implement get_ref() without code duplication for
const and non-const overloads
@tparam ThisType will be deduced as `basic_json` or `const basic_json`
- @throw std::domain_error if ReferenceType does not match underlying value
+ @throw type_error.303 if ReferenceType does not match underlying value
type of the current JSON
*/
template<typename ReferenceType, typename ThisType>
static ReferenceType get_ref_impl(ThisType& obj)
{
- // helper type
- using PointerType = typename std::add_pointer<ReferenceType>::type;
-
// delegate the call to get_ptr<>()
- auto ptr = obj.template get_ptr<PointerType>();
+ auto ptr = obj.template get_ptr<typename std::add_pointer<ReferenceType>::type>();
- if (ptr != nullptr)
+ if (JSON_LIKELY(ptr != nullptr))
{
return *ptr;
}
- else
- {
- throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " +
- obj.type_name());
- }
+
+ JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name())));
}
public:
-
/// @name value access
/// Direct access to the stored value of a JSON value.
/// @{
/*!
+ @brief get special-case overload
+
+ This overloads avoids a lot of template boilerplate, it can be seen as the
+ identity method
+
+ @tparam BasicJsonType == @ref basic_json
+
+ @return a copy of *this
+
+ @complexity Constant.
+
+ @since version 2.1.0
+ */
+ template<typename BasicJsonType, detail::enable_if_t<
+ std::is_same<typename std::remove_const<BasicJsonType>::type, basic_json_t>::value,
+ int> = 0>
+ basic_json get() const
+ {
+ return *this;
+ }
+
+ /*!
@brief get a value (explicit)
- Explicit type conversion between the JSON value and a compatible value.
+ Explicit type conversion between the JSON value and a compatible value
+ which is [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible)
+ and [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible).
+ The value is converted by calling the @ref json_serializer<ValueType>
+ `from_json()` method.
- @tparam ValueType non-pointer type compatible to the JSON value, for
- instance `int` for JSON integer numbers, `bool` for JSON booleans, or
- `std::vector` types for JSON arrays
+ The function is equivalent to executing
+ @code {.cpp}
+ ValueType ret;
+ JSONSerializer<ValueType>::from_json(*this, ret);
+ return ret;
+ @endcode
- @return copy of the JSON value, converted to type @a ValueType
+ This overloads is chosen if:
+ - @a ValueType is not @ref basic_json,
+ - @ref json_serializer<ValueType> has a `from_json()` method of the form
+ `void from_json(const basic_json&, ValueType&)`, and
+ - @ref json_serializer<ValueType> does not have a `from_json()` method of
+ the form `ValueType from_json(const basic_json&)`
- @throw std::domain_error in case passed type @a ValueType is incompatible
- to JSON; example: `"type must be object, but is null"`
+ @tparam ValueTypeCV the provided value type
+ @tparam ValueType the returned value type
- @complexity Linear in the size of the JSON value.
+ @return copy of the JSON value, converted to @a ValueType
+
+ @throw what @ref json_serializer<ValueType> `from_json()` method throws
@liveexample{The example below shows several conversions from JSON values
to other types. There a few things to note: (1) Floating-point numbers can
@@ -2881,21 +9655,71 @@ class basic_json
associative containers such as `std::unordered_map<std::string\,
json>`.,get__ValueType_const}
- @internal
- The idea of using a casted null pointer to choose the correct
- implementation is from <http://stackoverflow.com/a/8315197/266378>.
- @endinternal
+ @since version 2.1.0
+ */
+ template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,
+ detail::enable_if_t <
+ not std::is_same<basic_json_t, ValueType>::value and
+ detail::has_from_json<basic_json_t, ValueType>::value and
+ not detail::has_non_default_from_json<basic_json_t, ValueType>::value,
+ int> = 0>
+ ValueType get() const noexcept(noexcept(
+ JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>())))
+ {
+ // we cannot static_assert on ValueTypeCV being non-const, because
+ // there is support for get<const basic_json_t>(), which is why we
+ // still need the uncvref
+ static_assert(not std::is_reference<ValueTypeCV>::value,
+ "get() cannot be used with reference types, you might want to use get_ref()");
+ static_assert(std::is_default_constructible<ValueType>::value,
+ "types must be DefaultConstructible when used with get()");
- @sa @ref operator ValueType() const for implicit conversion
- @sa @ref get() for pointer-member access
+ ValueType ret;
+ JSONSerializer<ValueType>::from_json(*this, ret);
+ return ret;
+ }
- @since version 1.0.0
+ /*!
+ @brief get a value (explicit); special case
+
+ Explicit type conversion between the JSON value and a compatible value
+ which is **not** [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible)
+ and **not** [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible).
+ The value is converted by calling the @ref json_serializer<ValueType>
+ `from_json()` method.
+
+ The function is equivalent to executing
+ @code {.cpp}
+ return JSONSerializer<ValueTypeCV>::from_json(*this);
+ @endcode
+
+ This overloads is chosen if:
+ - @a ValueType is not @ref basic_json and
+ - @ref json_serializer<ValueType> has a `from_json()` method of the form
+ `ValueType from_json(const basic_json&)`
+
+ @note If @ref json_serializer<ValueType> has both overloads of
+ `from_json()`, this one is chosen.
+
+ @tparam ValueTypeCV the provided value type
+ @tparam ValueType the returned value type
+
+ @return copy of the JSON value, converted to @a ValueType
+
+ @throw what @ref json_serializer<ValueType> `from_json()` method throws
+
+ @since version 2.1.0
*/
- template<typename ValueType, typename std::enable_if<
- not std::is_pointer<ValueType>::value, int>::type = 0>
- ValueType get() const
+ template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,
+ detail::enable_if_t<not std::is_same<basic_json_t, ValueType>::value and
+ detail::has_non_default_from_json<basic_json_t, ValueType>::value,
+ int> = 0>
+ ValueType get() const noexcept(noexcept(
+ JSONSerializer<ValueTypeCV>::from_json(std::declval<const basic_json_t&>())))
{
- return get_impl(static_cast<ValueType*>(nullptr));
+ static_assert(not std::is_reference<ValueTypeCV>::value,
+ "get() cannot be used with reference types, you might want to use get_ref()");
+ return JSONSerializer<ValueTypeCV>::from_json(*this);
}
/*!
@@ -3019,13 +9843,13 @@ class basic_json
, "incompatible pointer type");
// delegate the call to get_impl_ptr<>() const
- return get_impl_ptr(static_cast<const PointerType>(nullptr));
+ return get_impl_ptr(static_cast<PointerType>(nullptr));
}
/*!
@brief get a reference value (implicit)
- Implict reference access to the internally stored JSON value. No copies
+ Implicit reference access to the internally stored JSON value. No copies
are made.
@warning Writing data to the referee of the result yields an undefined
@@ -3037,10 +9861,10 @@ class basic_json
@return reference to the internally stored JSON value if the requested
reference type @a ReferenceType fits to the JSON value; throws
- std::domain_error otherwise
+ type_error.303 otherwise
- @throw std::domain_error in case passed type @a ReferenceType is
- incompatible with the stored JSON value
+ @throw type_error.303 in case passed type @a ReferenceType is incompatible
+ with the stored JSON value; see example below
@complexity Constant.
@@ -3083,8 +9907,9 @@ class basic_json
@return copy of the JSON value, converted to type @a ValueType
- @throw std::domain_error in case passed type @a ValueType is incompatible
- to JSON, thrown by @ref get() const
+ @throw type_error.302 in case passed type @a ValueType is incompatible
+ to the JSON value type (e.g., the JSON value is of type boolean, but a
+ string is requested); see example below
@complexity Linear in the size of the JSON value.
@@ -3099,10 +9924,14 @@ class basic_json
*/
template < typename ValueType, typename std::enable_if <
not std::is_pointer<ValueType>::value and
+ not std::is_same<ValueType, detail::json_ref<basic_json>>::value and
not std::is_same<ValueType, typename string_t::value_type>::value
-#ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015
+#ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015
and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value
#endif
+#if defined(JSON_HAS_CPP_17)
+ and not std::is_same<ValueType, typename std::string_view>::value
+#endif
, int >::type = 0 >
operator ValueType() const
{
@@ -3131,36 +9960,40 @@ class basic_json
@return reference to the element at index @a idx
- @throw std::domain_error if the JSON value is not an array; example:
- `"cannot use at() with string"`
- @throw std::out_of_range if the index @a idx is out of range of the array;
- that is, `idx >= size()`; example: `"array index 7 is out of range"`
+ @throw type_error.304 if the JSON value is not an array; in this case,
+ calling `at` with an index makes no sense. See example below.
+ @throw out_of_range.401 if the index @a idx is out of range of the array;
+ that is, `idx >= size()`. See example below.
- @complexity Constant.
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes in the JSON value.
- @liveexample{The example below shows how array elements can be read and
- written using `at()`.,at__size_type}
+ @complexity Constant.
@since version 1.0.0
+
+ @liveexample{The example below shows how array elements can be read and
+ written using `at()`. It also demonstrates the different exceptions that
+ can be thrown.,at__size_type}
*/
reference at(size_type idx)
{
// at only works for arrays
- if (is_array())
+ if (JSON_LIKELY(is_array()))
{
- try
+ JSON_TRY
{
return m_value.array->at(idx);
}
- catch (std::out_of_range&)
+ JSON_CATCH (std::out_of_range&)
{
// create better exception explanation
- throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+ JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
}
}
else
{
- throw std::domain_error("cannot use at() with " + type_name());
+ JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name())));
}
}
@@ -3174,36 +10007,40 @@ class basic_json
@return const reference to the element at index @a idx
- @throw std::domain_error if the JSON value is not an array; example:
- `"cannot use at() with string"`
- @throw std::out_of_range if the index @a idx is out of range of the array;
- that is, `idx >= size()`; example: `"array index 7 is out of range"`
+ @throw type_error.304 if the JSON value is not an array; in this case,
+ calling `at` with an index makes no sense. See example below.
+ @throw out_of_range.401 if the index @a idx is out of range of the array;
+ that is, `idx >= size()`. See example below.
- @complexity Constant.
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes in the JSON value.
- @liveexample{The example below shows how array elements can be read using
- `at()`.,at__size_type_const}
+ @complexity Constant.
@since version 1.0.0
+
+ @liveexample{The example below shows how array elements can be read using
+ `at()`. It also demonstrates the different exceptions that can be thrown.,
+ at__size_type_const}
*/
const_reference at(size_type idx) const
{
// at only works for arrays
- if (is_array())
+ if (JSON_LIKELY(is_array()))
{
- try
+ JSON_TRY
{
return m_value.array->at(idx);
}
- catch (std::out_of_range&)
+ JSON_CATCH (std::out_of_range&)
{
// create better exception explanation
- throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+ JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
}
}
else
{
- throw std::domain_error("cannot use at() with " + type_name());
+ JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name())));
}
}
@@ -3217,40 +10054,44 @@ class basic_json
@return reference to the element at key @a key
- @throw std::domain_error if the JSON value is not an object; example:
- `"cannot use at() with boolean"`
- @throw std::out_of_range if the key @a key is is not stored in the object;
- that is, `find(key) == end()`; example: `"key "the fast" not found"`
+ @throw type_error.304 if the JSON value is not an object; in this case,
+ calling `at` with a key makes no sense. See example below.
+ @throw out_of_range.403 if the key @a key is is not stored in the object;
+ that is, `find(key) == end()`. See example below.
- @complexity Logarithmic in the size of the container.
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes in the JSON value.
- @liveexample{The example below shows how object elements can be read and
- written using `at()`.,at__object_t_key_type}
+ @complexity Logarithmic in the size of the container.
@sa @ref operator[](const typename object_t::key_type&) for unchecked
access by reference
@sa @ref value() for access by value with a default value
@since version 1.0.0
+
+ @liveexample{The example below shows how object elements can be read and
+ written using `at()`. It also demonstrates the different exceptions that
+ can be thrown.,at__object_t_key_type}
*/
reference at(const typename object_t::key_type& key)
{
// at only works for objects
- if (is_object())
+ if (JSON_LIKELY(is_object()))
{
- try
+ JSON_TRY
{
return m_value.object->at(key);
}
- catch (std::out_of_range&)
+ JSON_CATCH (std::out_of_range&)
{
// create better exception explanation
- throw std::out_of_range("key '" + key + "' not found");
+ JSON_THROW(out_of_range::create(403, "key '" + key + "' not found"));
}
}
else
{
- throw std::domain_error("cannot use at() with " + type_name());
+ JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name())));
}
}
@@ -3264,40 +10105,44 @@ class basic_json
@return const reference to the element at key @a key
- @throw std::domain_error if the JSON value is not an object; example:
- `"cannot use at() with boolean"`
- @throw std::out_of_range if the key @a key is is not stored in the object;
- that is, `find(key) == end()`; example: `"key "the fast" not found"`
+ @throw type_error.304 if the JSON value is not an object; in this case,
+ calling `at` with a key makes no sense. See example below.
+ @throw out_of_range.403 if the key @a key is is not stored in the object;
+ that is, `find(key) == end()`. See example below.
- @complexity Logarithmic in the size of the container.
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes in the JSON value.
- @liveexample{The example below shows how object elements can be read using
- `at()`.,at__object_t_key_type_const}
+ @complexity Logarithmic in the size of the container.
@sa @ref operator[](const typename object_t::key_type&) for unchecked
access by reference
@sa @ref value() for access by value with a default value
@since version 1.0.0
+
+ @liveexample{The example below shows how object elements can be read using
+ `at()`. It also demonstrates the different exceptions that can be thrown.,
+ at__object_t_key_type_const}
*/
const_reference at(const typename object_t::key_type& key) const
{
// at only works for objects
- if (is_object())
+ if (JSON_LIKELY(is_object()))
{
- try
+ JSON_TRY
{
return m_value.object->at(key);
}
- catch (std::out_of_range&)
+ JSON_CATCH (std::out_of_range&)
{
// create better exception explanation
- throw std::out_of_range("key '" + key + "' not found");
+ JSON_THROW(out_of_range::create(403, "key '" + key + "' not found"));
}
}
else
{
- throw std::domain_error("cannot use at() with " + type_name());
+ JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name())));
}
}
@@ -3314,8 +10159,8 @@ class basic_json
@return reference to the element at index @a idx
- @throw std::domain_error if JSON is not an array or null; example:
- `"cannot use operator[] with string"`
+ @throw type_error.305 if the JSON value is not an array or null; in that
+ cases, using the [] operator with an index makes no sense.
@complexity Constant if @a idx is in the range of the array. Otherwise
linear in `idx - size()`.
@@ -3337,7 +10182,7 @@ class basic_json
}
// operator[] only works for arrays
- if (is_array())
+ if (JSON_LIKELY(is_array()))
{
// fill up array with null values if given idx is outside range
if (idx >= m_value.array->size())
@@ -3349,10 +10194,8 @@ class basic_json
return m_value.array->operator[](idx);
}
- else
- {
- throw std::domain_error("cannot use operator[] with " + type_name());
- }
+
+ JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name())));
}
/*!
@@ -3364,8 +10207,8 @@ class basic_json
@return const reference to the element at index @a idx
- @throw std::domain_error if JSON is not an array; example: `"cannot use
- operator[] with null"`
+ @throw type_error.305 if the JSON value is not an array; in that case,
+ using the [] operator with an index makes no sense.
@complexity Constant.
@@ -3377,14 +10220,12 @@ class basic_json
const_reference operator[](size_type idx) const
{
// const operator[] only works for arrays
- if (is_array())
+ if (JSON_LIKELY(is_array()))
{
return m_value.array->operator[](idx);
}
- else
- {
- throw std::domain_error("cannot use operator[] with " + type_name());
- }
+
+ JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name())));
}
/*!
@@ -3400,8 +10241,8 @@ class basic_json
@return reference to the element at key @a key
- @throw std::domain_error if JSON is not an object or null; example:
- `"cannot use operator[] with string"`
+ @throw type_error.305 if the JSON value is not an object or null; in that
+ cases, using the [] operator with a key makes no sense.
@complexity Logarithmic in the size of the container.
@@ -3425,14 +10266,12 @@ class basic_json
}
// operator[] only works for objects
- if (is_object())
+ if (JSON_LIKELY(is_object()))
{
return m_value.object->operator[](key);
}
- else
- {
- throw std::domain_error("cannot use operator[] with " + type_name());
- }
+
+ JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name())));
}
/*!
@@ -3451,8 +10290,8 @@ class basic_json
@pre The element with key @a key must exist. **This precondition is
enforced with an assertion.**
- @throw std::domain_error if JSON is not an object; example: `"cannot use
- operator[] with null"`
+ @throw type_error.305 if the JSON value is not an object; in that case,
+ using the [] operator with a key makes no sense.
@complexity Logarithmic in the size of the container.
@@ -3468,83 +10307,13 @@ class basic_json
const_reference operator[](const typename object_t::key_type& key) const
{
// const operator[] only works for objects
- if (is_object())
+ if (JSON_LIKELY(is_object()))
{
assert(m_value.object->find(key) != m_value.object->end());
return m_value.object->find(key)->second;
}
- else
- {
- throw std::domain_error("cannot use operator[] with " + type_name());
- }
- }
-
- /*!
- @brief access specified object element
-
- Returns a reference to the element at with specified key @a key.
-
- @note If @a key is not found in the object, then it is silently added to
- the object and filled with a `null` value to make `key` a valid reference.
- In case the value was `null` before, it is converted to an object.
-
- @param[in] key key of the element to access
-
- @return reference to the element at key @a key
-
- @throw std::domain_error if JSON is not an object or null; example:
- `"cannot use operator[] with string"`
-
- @complexity Logarithmic in the size of the container.
-
- @liveexample{The example below shows how object elements can be read and
- written using the `[]` operator.,operatorarray__key_type}
- @sa @ref at(const typename object_t::key_type&) for access by reference
- with range checking
- @sa @ref value() for access by value with a default value
-
- @since version 1.0.0
- */
- template<typename T, std::size_t n>
- reference operator[](T * (&key)[n])
- {
- return operator[](static_cast<const T>(key));
- }
-
- /*!
- @brief read-only access specified object element
-
- Returns a const reference to the element at with specified key @a key. No
- bounds checking is performed.
-
- @warning If the element with key @a key does not exist, the behavior is
- undefined.
-
- @note This function is required for compatibility reasons with Clang.
-
- @param[in] key key of the element to access
-
- @return const reference to the element at key @a key
-
- @throw std::domain_error if JSON is not an object; example: `"cannot use
- operator[] with null"`
-
- @complexity Logarithmic in the size of the container.
-
- @liveexample{The example below shows how object elements can be read using
- the `[]` operator.,operatorarray__key_type_const}
-
- @sa @ref at(const typename object_t::key_type&) for access by reference
- with range checking
- @sa @ref value() for access by value with a default value
-
- @since version 1.0.0
- */
- template<typename T, std::size_t n>
- const_reference operator[](T * (&key)[n]) const
- {
- return operator[](static_cast<const T>(key));
+ JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name())));
}
/*!
@@ -3560,8 +10329,8 @@ class basic_json
@return reference to the element at key @a key
- @throw std::domain_error if JSON is not an object or null; example:
- `"cannot use operator[] with string"`
+ @throw type_error.305 if the JSON value is not an object or null; in that
+ cases, using the [] operator with a key makes no sense.
@complexity Logarithmic in the size of the container.
@@ -3586,14 +10355,12 @@ class basic_json
}
// at only works for objects
- if (is_object())
+ if (JSON_LIKELY(is_object()))
{
return m_value.object->operator[](key);
}
- else
- {
- throw std::domain_error("cannot use operator[] with " + type_name());
- }
+
+ JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name())));
}
/*!
@@ -3612,8 +10379,8 @@ class basic_json
@pre The element with key @a key must exist. **This precondition is
enforced with an assertion.**
- @throw std::domain_error if JSON is not an object; example: `"cannot use
- operator[] with null"`
+ @throw type_error.305 if the JSON value is not an object; in that case,
+ using the [] operator with a key makes no sense.
@complexity Logarithmic in the size of the container.
@@ -3630,15 +10397,13 @@ class basic_json
const_reference operator[](T* key) const
{
// at only works for objects
- if (is_object())
+ if (JSON_LIKELY(is_object()))
{
assert(m_value.object->find(key) != m_value.object->end());
return m_value.object->find(key)->second;
}
- else
- {
- throw std::domain_error("cannot use operator[] with " + type_name());
- }
+
+ JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name())));
}
/*!
@@ -3651,7 +10416,7 @@ class basic_json
@code {.cpp}
try {
return at(key);
- } catch(std::out_of_range) {
+ } catch(out_of_range) {
return default_value;
}
@endcode
@@ -3674,8 +10439,8 @@ class basic_json
@return copy of the element at key @a key or @a default_value if @a key
is not found
- @throw std::domain_error if JSON is not an object; example: `"cannot use
- value() with null"`
+ @throw type_error.306 if the JSON value is not an object; in that case,
+ using `value()` with a key makes no sense.
@complexity Logarithmic in the size of the container.
@@ -3691,10 +10456,10 @@ class basic_json
*/
template<class ValueType, typename std::enable_if<
std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>
- ValueType value(const typename object_t::key_type& key, ValueType default_value) const
+ ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const
{
// at only works for objects
- if (is_object())
+ if (JSON_LIKELY(is_object()))
{
// if key is found, return value and given default value otherwise
const auto it = find(key);
@@ -3702,15 +10467,11 @@ class basic_json
{
return *it;
}
- else
- {
- return default_value;
- }
- }
- else
- {
- throw std::domain_error("cannot use value() with " + type_name());
+
+ return default_value;
}
+
+ JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name())));
}
/*!
@@ -3732,7 +10493,7 @@ class basic_json
@code {.cpp}
try {
return at(ptr);
- } catch(std::out_of_range) {
+ } catch(out_of_range) {
return default_value;
}
@endcode
@@ -3751,8 +10512,8 @@ class basic_json
@return copy of the element at key @a key or @a default_value if @a key
is not found
- @throw std::domain_error if JSON is not an object; example: `"cannot use
- value() with null"`
+ @throw type_error.306 if the JSON value is not an objec; in that case,
+ using `value()` with a key makes no sense.
@complexity Logarithmic in the size of the container.
@@ -3765,25 +10526,23 @@ class basic_json
*/
template<class ValueType, typename std::enable_if<
std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>
- ValueType value(const json_pointer& ptr, ValueType default_value) const
+ ValueType value(const json_pointer& ptr, const ValueType& default_value) const
{
// at only works for objects
- if (is_object())
+ if (JSON_LIKELY(is_object()))
{
// if pointer resolves a value, return it or use default value
- try
+ JSON_TRY
{
return ptr.get_checked(this);
}
- catch (std::out_of_range&)
+ JSON_CATCH (out_of_range&)
{
return default_value;
}
}
- else
- {
- throw std::domain_error("cannot use value() with " + type_name());
- }
+
+ JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name())));
}
/*!
@@ -3812,7 +10571,7 @@ class basic_json
assertions**).
@post The JSON value remains unchanged.
- @throw std::out_of_range when called on `null` value
+ @throw invalid_iterator.214 when called on `null` value
@liveexample{The following code shows an example for `front()`.,front}
@@ -3855,7 +10614,8 @@ class basic_json
assertions**).
@post The JSON value remains unchanged.
- @throw std::out_of_range when called on `null` value.
+ @throw invalid_iterator.214 when called on a `null` value. See example
+ below.
@liveexample{The following code shows an example for `back()`.,back}
@@ -3899,17 +10659,18 @@ class basic_json
@post Invalidates iterators and references at or after the point of the
erase, including the `end()` iterator.
- @throw std::domain_error if called on a `null` value; example: `"cannot
- use erase() with null"`
- @throw std::domain_error if called on an iterator which does not belong to
- the current JSON value; example: `"iterator does not fit current value"`
- @throw std::out_of_range if called on a primitive type with invalid
+ @throw type_error.307 if called on a `null` value; example: `"cannot use
+ erase() with null"`
+ @throw invalid_iterator.202 if called on an iterator which does not belong
+ to the current JSON value; example: `"iterator does not fit current
+ value"`
+ @throw invalid_iterator.205 if called on a primitive type with invalid
iterator (i.e., any iterator which is not `begin()`); example: `"iterator
out of range"`
@complexity The complexity depends on the type:
- objects: amortized constant
- - arrays: linear in distance between pos and the end of the container
+ - arrays: linear in distance between @a pos and the end of the container
- strings: linear in the length of the string
- other types: constant
@@ -3932,9 +10693,9 @@ class basic_json
IteratorType erase(IteratorType pos)
{
// make sure iterator fits the current value
- if (this != pos.m_object)
+ if (JSON_UNLIKELY(this != pos.m_object))
{
- throw std::domain_error("iterator does not fit current value");
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
}
IteratorType result = end();
@@ -3947,16 +10708,16 @@ class basic_json
case value_t::number_unsigned:
case value_t::string:
{
- if (not pos.m_it.primitive_iterator.is_begin())
+ if (JSON_UNLIKELY(not pos.m_it.primitive_iterator.is_begin()))
{
- throw std::out_of_range("iterator out of range");
+ JSON_THROW(invalid_iterator::create(205, "iterator out of range"));
}
if (is_string())
{
AllocatorType<string_t> alloc;
- alloc.destroy(m_value.string);
- alloc.deallocate(m_value.string, 1);
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);
m_value.string = nullptr;
}
@@ -3978,9 +10739,7 @@ class basic_json
}
default:
- {
- throw std::domain_error("cannot use erase() with " + type_name());
- }
+ JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name())));
}
return result;
@@ -4006,11 +10765,11 @@ class basic_json
@post Invalidates iterators and references at or after the point of the
erase, including the `end()` iterator.
- @throw std::domain_error if called on a `null` value; example: `"cannot
- use erase() with null"`
- @throw std::domain_error if called on iterators which does not belong to
- the current JSON value; example: `"iterators do not fit current value"`
- @throw std::out_of_range if called on a primitive type with invalid
+ @throw type_error.307 if called on a `null` value; example: `"cannot use
+ erase() with null"`
+ @throw invalid_iterator.203 if called on iterators which does not belong
+ to the current JSON value; example: `"iterators do not fit current value"`
+ @throw invalid_iterator.204 if called on a primitive type with invalid
iterators (i.e., if `first != begin()` and `last != end()`); example:
`"iterators out of range"`
@@ -4039,9 +10798,9 @@ class basic_json
IteratorType erase(IteratorType first, IteratorType last)
{
// make sure iterator fits the current value
- if (this != first.m_object or this != last.m_object)
+ if (JSON_UNLIKELY(this != first.m_object or this != last.m_object))
{
- throw std::domain_error("iterators do not fit current value");
+ JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value"));
}
IteratorType result = end();
@@ -4054,16 +10813,17 @@ class basic_json
case value_t::number_unsigned:
case value_t::string:
{
- if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end())
+ if (JSON_LIKELY(not first.m_it.primitive_iterator.is_begin()
+ or not last.m_it.primitive_iterator.is_end()))
{
- throw std::out_of_range("iterators out of range");
+ JSON_THROW(invalid_iterator::create(204, "iterators out of range"));
}
if (is_string())
{
AllocatorType<string_t> alloc;
- alloc.destroy(m_value.string);
- alloc.deallocate(m_value.string, 1);
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);
m_value.string = nullptr;
}
@@ -4087,9 +10847,7 @@ class basic_json
}
default:
- {
- throw std::domain_error("cannot use erase() with " + type_name());
- }
+ JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name())));
}
return result;
@@ -4109,7 +10867,7 @@ class basic_json
@post References and iterators to the erased elements are invalidated.
Other references and iterators are not affected.
- @throw std::domain_error when called on a type other than JSON object;
+ @throw type_error.307 when called on a type other than JSON object;
example: `"cannot use erase() with null"`
@complexity `log(size()) + count(key)`
@@ -4127,14 +10885,12 @@ class basic_json
size_type erase(const typename object_t::key_type& key)
{
// this erase only works for objects
- if (is_object())
+ if (JSON_LIKELY(is_object()))
{
return m_value.object->erase(key);
}
- else
- {
- throw std::domain_error("cannot use erase() with " + type_name());
- }
+
+ JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name())));
}
/*!
@@ -4144,9 +10900,9 @@ class basic_json
@param[in] idx index of the element to remove
- @throw std::domain_error when called on a type other than JSON array;
+ @throw type_error.307 when called on a type other than JSON object;
example: `"cannot use erase() with null"`
- @throw std::out_of_range when `idx >= size()`; example: `"array index 17
+ @throw out_of_range.401 when `idx >= size()`; example: `"array index 17
is out of range"`
@complexity Linear in distance between @a idx and the end of the container.
@@ -4164,18 +10920,18 @@ class basic_json
void erase(const size_type idx)
{
// this erase only works for arrays
- if (is_array())
+ if (JSON_LIKELY(is_array()))
{
- if (idx >= size())
+ if (JSON_UNLIKELY(idx >= size()))
{
- throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+ JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
}
m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));
}
else
{
- throw std::domain_error("cannot use erase() with " + type_name());
+ JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name())));
}
}
@@ -4199,7 +10955,7 @@ class basic_json
@note This method always returns @ref end() when executed on a JSON type
that is not an object.
- @param[in] key key value of the element to search for
+ @param[in] key key value of the element to search for.
@return Iterator to an element with key equivalent to @a key. If no such
element is found or the JSON value is not an object, past-the-end (see
@@ -4211,13 +10967,14 @@ class basic_json
@since version 1.0.0
*/
- iterator find(typename object_t::key_type key)
+ template<typename KeyT>
+ iterator find(KeyT&& key)
{
auto result = end();
if (is_object())
{
- result.m_it.object_iterator = m_value.object->find(key);
+ result.m_it.object_iterator = m_value.object->find(std::forward<KeyT>(key));
}
return result;
@@ -4225,15 +10982,16 @@ class basic_json
/*!
@brief find an element in a JSON object
- @copydoc find(typename object_t::key_type)
+ @copydoc find(KeyT&&)
*/
- const_iterator find(typename object_t::key_type key) const
+ template<typename KeyT>
+ const_iterator find(KeyT&& key) const
{
auto result = cend();
if (is_object())
{
- result.m_it.object_iterator = m_value.object->find(key);
+ result.m_it.object_iterator = m_value.object->find(std::forward<KeyT>(key));
}
return result;
@@ -4260,10 +11018,11 @@ class basic_json
@since version 1.0.0
*/
- size_type count(typename object_t::key_type key) const
+ template<typename KeyT>
+ size_type count(KeyT&& key) const
{
// return 0 for all nonobject types
- return is_object() ? m_value.object->count(key) : 0;
+ return is_object() ? m_value.object->count(std::forward<KeyT>(key)) : 0;
}
/// @}
@@ -4549,10 +11308,6 @@ class basic_json
return const_reverse_iterator(cbegin());
}
- private:
- // forward declaration
- template<typename IteratorType> class iteration_proxy;
-
public:
/*!
@brief wrapper to access iterator member functions in range-based for
@@ -4562,20 +11317,62 @@ class basic_json
reference to the JSON values is returned, so there is no access to the
underlying iterator.
+ For loop without iterator_wrapper:
+
+ @code{cpp}
+ for (auto it = j_object.begin(); it != j_object.end(); ++it)
+ {
+ std::cout << "key: " << it.key() << ", value:" << it.value() << '\n';
+ }
+ @endcode
+
+ Range-based for loop without iterator proxy:
+
+ @code{cpp}
+ for (auto it : j_object)
+ {
+ // "it" is of type json::reference and has no key() member
+ std::cout << "value: " << it << '\n';
+ }
+ @endcode
+
+ Range-based for loop with iterator proxy:
+
+ @code{cpp}
+ for (auto it : json::iterator_wrapper(j_object))
+ {
+ std::cout << "key: " << it.key() << ", value:" << it.value() << '\n';
+ }
+ @endcode
+
+ @note When iterating over an array, `key()` will return the index of the
+ element as string (see example).
+
+ @param[in] ref reference to a JSON value
+ @return iteration proxy object wrapping @a ref with an interface to use in
+ range-based for loops
+
+ @liveexample{The following code shows how the wrapper is used,iterator_wrapper}
+
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes in the JSON value.
+
+ @complexity Constant.
+
@note The name of this function is not yet final and may change in the
future.
*/
- static iteration_proxy<iterator> iterator_wrapper(reference cont)
+ static iteration_proxy<iterator> iterator_wrapper(reference ref)
{
- return iteration_proxy<iterator>(cont);
+ return iteration_proxy<iterator>(ref);
}
/*!
@copydoc iterator_wrapper(reference)
*/
- static iteration_proxy<const_iterator> iterator_wrapper(const_reference cont)
+ static iteration_proxy<const_iterator> iterator_wrapper(const_reference ref)
{
- return iteration_proxy<const_iterator>(cont);
+ return iteration_proxy<const_iterator>(ref);
}
/// @}
@@ -4589,9 +11386,9 @@ class basic_json
/// @{
/*!
- @brief checks whether the container is empty
+ @brief checks whether the container is empty.
- Checks if a JSON value has no elements.
+ Checks if a JSON value has no elements (i.e. whether its @ref size is `0`).
@return The return value depends on the different types and is
defined as follows:
@@ -4604,23 +11401,27 @@ class basic_json
object | result of function `object_t::empty()`
array | result of function `array_t::empty()`
- @note This function does not return whether a string stored as JSON value
- is empty - it returns whether the JSON container itself is empty which is
- false in the case of a string.
+ @liveexample{The following code uses `empty()` to check if a JSON
+ object contains any elements.,empty}
@complexity Constant, as long as @ref array_t and @ref object_t satisfy
the Container concept; that is, their `empty()` functions have constant
complexity.
+ @iterators No changes.
+
+ @exceptionsafety No-throw guarantee: this function never throws exceptions.
+
+ @note This function does not return whether a string stored as JSON value
+ is empty - it returns whether the JSON container itself is empty which is
+ false in the case of a string.
+
@requirement This function helps `basic_json` satisfying the
[Container](http://en.cppreference.com/w/cpp/concept/Container)
requirements:
- The complexity is constant.
- Has the semantics of `begin() == end()`.
- @liveexample{The following code uses `empty()` to check if a JSON
- object contains any elements.,empty}
-
@sa @ref size() -- returns the number of elements
@since version 1.0.0
@@ -4671,23 +11472,27 @@ class basic_json
object | result of function object_t::size()
array | result of function array_t::size()
- @note This function does not return the length of a string stored as JSON
- value - it returns the number of elements in the JSON value which is 1 in
- the case of a string.
+ @liveexample{The following code calls `size()` on the different value
+ types.,size}
@complexity Constant, as long as @ref array_t and @ref object_t satisfy
the Container concept; that is, their size() functions have constant
complexity.
+ @iterators No changes.
+
+ @exceptionsafety No-throw guarantee: this function never throws exceptions.
+
+ @note This function does not return the length of a string stored as JSON
+ value - it returns the number of elements in the JSON value which is 1 in
+ the case of a string.
+
@requirement This function helps `basic_json` satisfying the
[Container](http://en.cppreference.com/w/cpp/concept/Container)
requirements:
- The complexity is constant.
- Has the semantics of `std::distance(begin(), end())`.
- @liveexample{The following code calls `size()` on the different value
- types.,size}
-
@sa @ref empty() -- checks whether the container is empty
@sa @ref max_size() -- returns the maximal number of elements
@@ -4741,10 +11546,17 @@ class basic_json
object | result of function `object_t::max_size()`
array | result of function `array_t::max_size()`
+ @liveexample{The following code calls `max_size()` on the different value
+ types. Note the output is implementation specific.,max_size}
+
@complexity Constant, as long as @ref array_t and @ref object_t satisfy
the Container concept; that is, their `max_size()` functions have constant
complexity.
+ @iterators No changes.
+
+ @exceptionsafety No-throw guarantee: this function never throws exceptions.
+
@requirement This function helps `basic_json` satisfying the
[Container](http://en.cppreference.com/w/cpp/concept/Container)
requirements:
@@ -4752,9 +11564,6 @@ class basic_json
- Has the semantics of returning `b.size()` where `b` is the largest
possible JSON value.
- @liveexample{The following code calls `max_size()` on the different value
- types. Note the output is implementation specific.,max_size}
-
@sa @ref size() -- returns the number of elements
@since version 1.0.0
@@ -4797,7 +11606,8 @@ class basic_json
@brief clears the contents
Clears the content of a JSON value and resets it to the default value as
- if @ref basic_json(value_t) would have been called:
+ if @ref basic_json(value_t) would have been called with the current value
+ type from @ref type():
Value type | initial value
----------- | -------------
@@ -4808,11 +11618,24 @@ class basic_json
object | `{}`
array | `[]`
- @complexity Linear in the size of the JSON value.
+ @post Has the same effect as calling
+ @code {.cpp}
+ *this = basic_json(type());
+ @endcode
@liveexample{The example below shows the effect of `clear()` to different
JSON types.,clear}
+ @complexity Linear in the size of the JSON value.
+
+ @iterators All iterators, pointers and references related to this container
+ are invalidated.
+
+ @exceptionsafety No-throw guarantee: this function never throws exceptions.
+
+ @sa @ref basic_json(value_t) -- constructor that creates an object with the
+ same value than calling `clear()`
+
@since version 1.0.0
*/
void clear() noexcept
@@ -4862,9 +11685,7 @@ class basic_json
}
default:
- {
break;
- }
}
}
@@ -4877,7 +11698,7 @@ class basic_json
@param[in] val the value to add to the JSON array
- @throw std::domain_error when called on a type other than JSON array or
+ @throw type_error.308 when called on a type other than JSON array or
null; example: `"cannot use push_back() with number"`
@complexity Amortized constant.
@@ -4891,9 +11712,9 @@ class basic_json
void push_back(basic_json&& val)
{
// push_back only works for null objects or arrays
- if (not(is_null() or is_array()))
+ if (JSON_UNLIKELY(not(is_null() or is_array())))
{
- throw std::domain_error("cannot use push_back() with " + type_name());
+ JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name())));
}
// transform null object into an array
@@ -4927,9 +11748,9 @@ class basic_json
void push_back(const basic_json& val)
{
// push_back only works for null objects or arrays
- if (not(is_null() or is_array()))
+ if (JSON_UNLIKELY(not(is_null() or is_array())))
{
- throw std::domain_error("cannot use push_back() with " + type_name());
+ JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name())));
}
// transform null object into an array
@@ -4963,7 +11784,7 @@ class basic_json
@param[in] val the value to add to the JSON object
- @throw std::domain_error when called on a type other than JSON object or
+ @throw type_error.308 when called on a type other than JSON object or
null; example: `"cannot use push_back() with number"`
@complexity Logarithmic in the size of the container, O(log(`size()`)).
@@ -4977,9 +11798,9 @@ class basic_json
void push_back(const typename object_t::value_type& val)
{
// push_back only works for null objects or objects
- if (not(is_null() or is_object()))
+ if (JSON_UNLIKELY(not(is_null() or is_object())))
{
- throw std::domain_error("cannot use push_back() with " + type_name());
+ JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name())));
}
// transform null object into an object
@@ -5017,7 +11838,7 @@ class basic_json
@ref push_back(const typename object_t::value_type&). Otherwise, @a init
is converted to a JSON value and added using @ref push_back(basic_json&&).
- @param init an initializer list
+ @param[in] init an initializer list
@complexity Linear in the size of the initializer list @a init.
@@ -5029,12 +11850,13 @@ class basic_json
@liveexample{The example shows how initializer lists are treated as
objects when possible.,push_back__initializer_list}
*/
- void push_back(std::initializer_list<basic_json> init)
+ void push_back(initializer_list_t init)
{
- if (is_object() and init.size() == 2 and init.begin()->is_string())
+ if (is_object() and init.size() == 2 and (*init.begin())->is_string())
{
- const string_t key = *init.begin();
- push_back(typename object_t::value_type(key, *(init.begin() + 1)));
+ basic_json&& key = init.begin()->moved_or_copied();
+ push_back(typename object_t::value_type(
+ std::move(key.get_ref<string_t&>()), (init.begin() + 1)->moved_or_copied()));
}
else
{
@@ -5044,9 +11866,9 @@ class basic_json
/*!
@brief add an object to an object
- @copydoc push_back(std::initializer_list<basic_json>)
+ @copydoc push_back(initializer_list_t)
*/
- reference operator+=(std::initializer_list<basic_json> init)
+ reference operator+=(initializer_list_t init)
{
push_back(init);
return *this;
@@ -5062,7 +11884,7 @@ class basic_json
@param[in] args arguments to forward to a constructor of @ref basic_json
@tparam Args compatible types to create a @ref basic_json object
- @throw std::domain_error when called on a type other than JSON array or
+ @throw type_error.311 when called on a type other than JSON array or
null; example: `"cannot use emplace_back() with number"`
@complexity Amortized constant.
@@ -5077,9 +11899,9 @@ class basic_json
void emplace_back(Args&& ... args)
{
// emplace_back only works for null objects or arrays
- if (not(is_null() or is_array()))
+ if (JSON_UNLIKELY(not(is_null() or is_array())))
{
- throw std::domain_error("cannot use emplace_back() with " + type_name());
+ JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name())));
}
// transform null object into an array
@@ -5097,8 +11919,8 @@ class basic_json
/*!
@brief add an object to an object if key does not exist
- Inserts a new element into a JSON object constructed in-place with the given
- @a args if there is no element with the key in the container. If the
+ Inserts a new element into a JSON object constructed in-place with the
+ given @a args if there is no element with the key in the container. If the
function is called on a JSON null value, an empty object is created before
appending the value created from @a args.
@@ -5109,7 +11931,7 @@ class basic_json
already-existing element if no insertion happened, and a bool
denoting whether the insertion took place.
- @throw std::domain_error when called on a type other than JSON object or
+ @throw type_error.311 when called on a type other than JSON object or
null; example: `"cannot use emplace() with number"`
@complexity Logarithmic in the size of the container, O(log(`size()`)).
@@ -5125,9 +11947,9 @@ class basic_json
std::pair<iterator, bool> emplace(Args&& ... args)
{
// emplace only works for null objects or arrays
- if (not(is_null() or is_object()))
+ if (JSON_UNLIKELY(not(is_null() or is_object())))
{
- throw std::domain_error("cannot use emplace() with " + type_name());
+ JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name())));
}
// transform null object into an object
@@ -5158,13 +11980,13 @@ class basic_json
@param[in] val element to insert
@return iterator pointing to the inserted @a val.
- @throw std::domain_error if called on JSON values other than arrays;
+ @throw type_error.309 if called on JSON values other than arrays;
example: `"cannot use insert() with string"`
- @throw std::domain_error if @a pos is not an iterator of *this; example:
- `"iterator does not fit current value"`
+ @throw invalid_iterator.202 if @a pos is not an iterator of *this;
+ example: `"iterator does not fit current value"`
- @complexity Constant plus linear in the distance between pos and end of the
- container.
+ @complexity Constant plus linear in the distance between @a pos and end of
+ the container.
@liveexample{The example shows how `insert()` is used.,insert}
@@ -5173,12 +11995,12 @@ class basic_json
iterator insert(const_iterator pos, const basic_json& val)
{
// insert only works for arrays
- if (is_array())
+ if (JSON_LIKELY(is_array()))
{
// check if iterator pos fits to this JSON value
- if (pos.m_object != this)
+ if (JSON_UNLIKELY(pos.m_object != this))
{
- throw std::domain_error("iterator does not fit current value");
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
}
// insert to array and return iterator
@@ -5186,10 +12008,8 @@ class basic_json
result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val);
return result;
}
- else
- {
- throw std::domain_error("cannot use insert() with " + type_name());
- }
+
+ JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
}
/*!
@@ -5213,10 +12033,10 @@ class basic_json
@return iterator pointing to the first element inserted, or @a pos if
`cnt==0`
- @throw std::domain_error if called on JSON values other than arrays;
- example: `"cannot use insert() with string"`
- @throw std::domain_error if @a pos is not an iterator of *this; example:
- `"iterator does not fit current value"`
+ @throw type_error.309 if called on JSON values other than arrays; example:
+ `"cannot use insert() with string"`
+ @throw invalid_iterator.202 if @a pos is not an iterator of *this;
+ example: `"iterator does not fit current value"`
@complexity Linear in @a cnt plus linear in the distance between @a pos
and end of the container.
@@ -5228,12 +12048,12 @@ class basic_json
iterator insert(const_iterator pos, size_type cnt, const basic_json& val)
{
// insert only works for arrays
- if (is_array())
+ if (JSON_LIKELY(is_array()))
{
// check if iterator pos fits to this JSON value
- if (pos.m_object != this)
+ if (JSON_UNLIKELY(pos.m_object != this))
{
- throw std::domain_error("iterator does not fit current value");
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
}
// insert to array and return iterator
@@ -5241,10 +12061,8 @@ class basic_json
result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);
return result;
}
- else
- {
- throw std::domain_error("cannot use insert() with " + type_name());
- }
+
+ JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
}
/*!
@@ -5257,13 +12075,13 @@ class basic_json
@param[in] first begin of the range of elements to insert
@param[in] last end of the range of elements to insert
- @throw std::domain_error if called on JSON values other than arrays;
- example: `"cannot use insert() with string"`
- @throw std::domain_error if @a pos is not an iterator of *this; example:
- `"iterator does not fit current value"`
- @throw std::domain_error if @a first and @a last do not belong to the same
- JSON value; example: `"iterators do not fit"`
- @throw std::domain_error if @a first or @a last are iterators into
+ @throw type_error.309 if called on JSON values other than arrays; example:
+ `"cannot use insert() with string"`
+ @throw invalid_iterator.202 if @a pos is not an iterator of *this;
+ example: `"iterator does not fit current value"`
+ @throw invalid_iterator.210 if @a first and @a last do not belong to the
+ same JSON value; example: `"iterators do not fit"`
+ @throw invalid_iterator.211 if @a first or @a last are iterators into
container for which insert is called; example: `"passed iterators may not
belong to container"`
@@ -5280,26 +12098,26 @@ class basic_json
iterator insert(const_iterator pos, const_iterator first, const_iterator last)
{
// insert only works for arrays
- if (not is_array())
+ if (JSON_UNLIKELY(not is_array()))
{
- throw std::domain_error("cannot use insert() with " + type_name());
+ JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
}
// check if iterator pos fits to this JSON value
- if (pos.m_object != this)
+ if (JSON_UNLIKELY(pos.m_object != this))
{
- throw std::domain_error("iterator does not fit current value");
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
}
// check if range iterators belong to the same JSON object
- if (first.m_object != last.m_object)
+ if (JSON_UNLIKELY(first.m_object != last.m_object))
{
- throw std::domain_error("iterators do not fit");
+ JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
}
- if (first.m_object == this or last.m_object == this)
+ if (JSON_UNLIKELY(first.m_object == this))
{
- throw std::domain_error("passed iterators may not belong to container");
+ JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container"));
}
// insert to array and return iterator
@@ -5320,10 +12138,10 @@ class basic_json
the end() iterator
@param[in] ilist initializer list to insert the values from
- @throw std::domain_error if called on JSON values other than arrays;
- example: `"cannot use insert() with string"`
- @throw std::domain_error if @a pos is not an iterator of *this; example:
- `"iterator does not fit current value"`
+ @throw type_error.309 if called on JSON values other than arrays; example:
+ `"cannot use insert() with string"`
+ @throw invalid_iterator.202 if @a pos is not an iterator of *this;
+ example: `"iterator does not fit current value"`
@return iterator pointing to the first element inserted, or @a pos if
`ilist` is empty
@@ -5335,27 +12153,177 @@ class basic_json
@since version 1.0.0
*/
- iterator insert(const_iterator pos, std::initializer_list<basic_json> ilist)
+ iterator insert(const_iterator pos, initializer_list_t ilist)
{
// insert only works for arrays
- if (not is_array())
+ if (JSON_UNLIKELY(not is_array()))
{
- throw std::domain_error("cannot use insert() with " + type_name());
+ JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
}
// check if iterator pos fits to this JSON value
- if (pos.m_object != this)
+ if (JSON_UNLIKELY(pos.m_object != this))
{
- throw std::domain_error("iterator does not fit current value");
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
}
// insert to array and return iterator
iterator result(this);
- result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist);
+ result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist.begin(), ilist.end());
return result;
}
/*!
+ @brief inserts elements
+
+ Inserts elements from range `[first, last)`.
+
+ @param[in] first begin of the range of elements to insert
+ @param[in] last end of the range of elements to insert
+
+ @throw type_error.309 if called on JSON values other than objects; example:
+ `"cannot use insert() with string"`
+ @throw invalid_iterator.202 if iterator @a first or @a last does does not
+ point to an object; example: `"iterators first and last must point to
+ objects"`
+ @throw invalid_iterator.210 if @a first and @a last do not belong to the
+ same JSON value; example: `"iterators do not fit"`
+
+ @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number
+ of elements to insert.
+
+ @liveexample{The example shows how `insert()` is used.,insert__range_object}
+
+ @since version 3.0.0
+ */
+ void insert(const_iterator first, const_iterator last)
+ {
+ // insert only works for objects
+ if (JSON_UNLIKELY(not is_object()))
+ {
+ JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
+ }
+
+ // check if range iterators belong to the same JSON object
+ if (JSON_UNLIKELY(first.m_object != last.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
+ }
+
+ // passed iterators must belong to objects
+ if (JSON_UNLIKELY(not first.m_object->is_object()))
+ {
+ JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects"));
+ }
+
+ m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);
+ }
+
+ /*!
+ @brief updates a JSON object from another object, overwriting existing keys
+
+ Inserts all values from JSON object @a j and overwrites existing keys.
+
+ @param[in] j JSON object to read values from
+
+ @throw type_error.312 if called on JSON values other than objects; example:
+ `"cannot use update() with string"`
+
+ @complexity O(N*log(size() + N)), where N is the number of elements to
+ insert.
+
+ @liveexample{The example shows how `update()` is used.,update}
+
+ @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update
+
+ @since version 3.0.0
+ */
+ void update(const_reference j)
+ {
+ // implicitly convert null value to an empty object
+ if (is_null())
+ {
+ m_type = value_t::object;
+ m_value.object = create<object_t>();
+ assert_invariant();
+ }
+
+ if (JSON_UNLIKELY(not is_object()))
+ {
+ JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name())));
+ }
+ if (JSON_UNLIKELY(not j.is_object()))
+ {
+ JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name())));
+ }
+
+ for (auto it = j.cbegin(); it != j.cend(); ++it)
+ {
+ m_value.object->operator[](it.key()) = it.value();
+ }
+ }
+
+ /*!
+ @brief updates a JSON object from another object, overwriting existing keys
+
+ Inserts all values from from range `[first, last)` and overwrites existing
+ keys.
+
+ @param[in] first begin of the range of elements to insert
+ @param[in] last end of the range of elements to insert
+
+ @throw type_error.312 if called on JSON values other than objects; example:
+ `"cannot use update() with string"`
+ @throw invalid_iterator.202 if iterator @a first or @a last does does not
+ point to an object; example: `"iterators first and last must point to
+ objects"`
+ @throw invalid_iterator.210 if @a first and @a last do not belong to the
+ same JSON value; example: `"iterators do not fit"`
+
+ @complexity O(N*log(size() + N)), where N is the number of elements to
+ insert.
+
+ @liveexample{The example shows how `update()` is used__range.,update}
+
+ @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update
+
+ @since version 3.0.0
+ */
+ void update(const_iterator first, const_iterator last)
+ {
+ // implicitly convert null value to an empty object
+ if (is_null())
+ {
+ m_type = value_t::object;
+ m_value.object = create<object_t>();
+ assert_invariant();
+ }
+
+ if (JSON_UNLIKELY(not is_object()))
+ {
+ JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name())));
+ }
+
+ // check if range iterators belong to the same JSON object
+ if (JSON_UNLIKELY(first.m_object != last.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
+ }
+
+ // passed iterators must belong to objects
+ if (JSON_UNLIKELY(not first.m_object->is_object()
+ or not first.m_object->is_object()))
+ {
+ JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects"));
+ }
+
+ for (auto it = first; it != last; ++it)
+ {
+ m_value.object->operator[](it.key()) = it.value();
+ }
+ }
+
+ /*!
@brief exchanges the values
Exchanges the contents of the JSON value with those of @a other. Does not
@@ -5394,7 +12362,7 @@ class basic_json
@param[in,out] other array to exchange the contents with
- @throw std::domain_error when JSON value is not an array; example: `"cannot
+ @throw type_error.310 when JSON value is not an array; example: `"cannot
use swap() with string"`
@complexity Constant.
@@ -5407,13 +12375,13 @@ class basic_json
void swap(array_t& other)
{
// swap only works for arrays
- if (is_array())
+ if (JSON_LIKELY(is_array()))
{
std::swap(*(m_value.array), other);
}
else
{
- throw std::domain_error("cannot use swap() with " + type_name());
+ JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name())));
}
}
@@ -5427,7 +12395,7 @@ class basic_json
@param[in,out] other object to exchange the contents with
- @throw std::domain_error when JSON value is not an object; example:
+ @throw type_error.310 when JSON value is not an object; example:
`"cannot use swap() with string"`
@complexity Constant.
@@ -5440,13 +12408,13 @@ class basic_json
void swap(object_t& other)
{
// swap only works for objects
- if (is_object())
+ if (JSON_LIKELY(is_object()))
{
std::swap(*(m_value.object), other);
}
else
{
- throw std::domain_error("cannot use swap() with " + type_name());
+ JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name())));
}
}
@@ -5460,7 +12428,7 @@ class basic_json
@param[in,out] other string to exchange the contents with
- @throw std::domain_error when JSON value is not a string; example: `"cannot
+ @throw type_error.310 when JSON value is not a string; example: `"cannot
use swap() with boolean"`
@complexity Constant.
@@ -5473,19 +12441,19 @@ class basic_json
void swap(string_t& other)
{
// swap only works for strings
- if (is_string())
+ if (JSON_LIKELY(is_string()))
{
std::swap(*(m_value.string), other);
}
else
{
- throw std::domain_error("cannot use swap() with " + type_name());
+ JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name())));
}
}
/// @}
-
+ public:
//////////////////////////////////////////
// lexicographical comparison operators //
//////////////////////////////////////////
@@ -5493,56 +12461,38 @@ class basic_json
/// @name lexicographical comparison operators
/// @{
- private:
- /*!
- @brief comparison operator for JSON types
-
- Returns an ordering that is similar to Python:
- - order: null < boolean < number < object < array < string
- - furthermore, each type is not smaller than itself
-
- @since version 1.0.0
- */
- friend bool operator<(const value_t lhs, const value_t rhs) noexcept
- {
- static constexpr std::array<uint8_t, 8> order = {{
- 0, // null
- 3, // object
- 4, // array
- 5, // string
- 1, // boolean
- 2, // integer
- 2, // unsigned
- 2, // float
- }
- };
-
- // discarded values are not comparable
- if (lhs == value_t::discarded or rhs == value_t::discarded)
- {
- return false;
- }
-
- return order[static_cast<std::size_t>(lhs)] < order[static_cast<std::size_t>(rhs)];
- }
-
- public:
/*!
@brief comparison: equal
Compares two JSON values for equality according to the following rules:
- Two JSON values are equal if (1) they are from the same type and (2)
- their stored values are the same.
+ their stored values are the same according to their respective
+ `operator==`.
- Integer and floating-point numbers are automatically converted before
- comparison. Floating-point numbers are compared indirectly: two
- floating-point numbers `f1` and `f2` are considered equal if neither
- `f1 > f2` nor `f2 > f1` holds.
+ comparison. Note than two NaN values are always treated as unequal.
- Two JSON null values are equal.
+ @note Floating-point inside JSON values numbers are compared with
+ `json::number_float_t::operator==` which is `double::operator==` by
+ default. To compare floating-point while respecting an epsilon, an alternative
+ [comparison function](https://github.com/mariokonrad/marnav/blob/master/src/marnav/math/floatingpoint.hpp#L34-#L39)
+ could be used, for instance
+ @code {.cpp}
+ template<typename T, typename = typename std::enable_if<std::is_floating_point<T>::value, T>::type>
+ inline bool is_same(T a, T b, T epsilon = std::numeric_limits<T>::epsilon()) noexcept
+ {
+ return std::abs(a - b) <= epsilon;
+ }
+ @endcode
+
+ @note NaN values never compare equal to themselves or to other NaN values.
+
@param[in] lhs first JSON value to consider
@param[in] rhs second JSON value to consider
@return whether the values @a lhs and @a rhs are equal
+ @exceptionsafety No-throw guarantee: this function never throws exceptions.
+
@complexity Linear.
@liveexample{The example demonstrates comparing several JSON
@@ -5560,66 +12510,56 @@ class basic_json
switch (lhs_type)
{
case value_t::array:
- {
- return *lhs.m_value.array == *rhs.m_value.array;
- }
+ return (*lhs.m_value.array == *rhs.m_value.array);
+
case value_t::object:
- {
- return *lhs.m_value.object == *rhs.m_value.object;
- }
+ return (*lhs.m_value.object == *rhs.m_value.object);
+
case value_t::null:
- {
return true;
- }
+
case value_t::string:
- {
- return *lhs.m_value.string == *rhs.m_value.string;
- }
+ return (*lhs.m_value.string == *rhs.m_value.string);
+
case value_t::boolean:
- {
- return lhs.m_value.boolean == rhs.m_value.boolean;
- }
+ return (lhs.m_value.boolean == rhs.m_value.boolean);
+
case value_t::number_integer:
- {
- return lhs.m_value.number_integer == rhs.m_value.number_integer;
- }
+ return (lhs.m_value.number_integer == rhs.m_value.number_integer);
+
case value_t::number_unsigned:
- {
- return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned;
- }
+ return (lhs.m_value.number_unsigned == rhs.m_value.number_unsigned);
+
case value_t::number_float:
- {
- return lhs.m_value.number_float == rhs.m_value.number_float;
- }
+ return (lhs.m_value.number_float == rhs.m_value.number_float);
+
default:
- {
return false;
- }
}
}
else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float)
{
- return static_cast<number_float_t>(lhs.m_value.number_integer) == rhs.m_value.number_float;
+ return (static_cast<number_float_t>(lhs.m_value.number_integer) == rhs.m_value.number_float);
}
else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer)
{
- return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer);
+ return (lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer));
}
else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float)
{
- return static_cast<number_float_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_float;
+ return (static_cast<number_float_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_float);
}
else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned)
{
- return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_unsigned);
+ return (lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_unsigned));
}
else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer)
{
- return static_cast<number_integer_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer;
+ return (static_cast<number_integer_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer);
}
else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned)
{
- return lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned);
+ return (lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned));
}
return false;
@@ -5627,34 +12567,24 @@ class basic_json
/*!
@brief comparison: equal
-
- The functions compares the given JSON value against a null pointer. As the
- null pointer can be used to initialize a JSON value to null, a comparison
- of JSON value @a v with a null pointer should be equivalent to call
- `v.is_null()`.
-
- @param[in] v JSON value to consider
- @return whether @a v is null
-
- @complexity Constant.
-
- @liveexample{The example compares several JSON types to the null pointer.
- ,operator__equal__nullptr_t}
-
- @since version 1.0.0
+ @copydoc operator==(const_reference, const_reference)
*/
- friend bool operator==(const_reference v, std::nullptr_t) noexcept
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept
{
- return v.is_null();
+ return (lhs == basic_json(rhs));
}
/*!
@brief comparison: equal
- @copydoc operator==(const_reference, std::nullptr_t)
+ @copydoc operator==(const_reference, const_reference)
*/
- friend bool operator==(std::nullptr_t, const_reference v) noexcept
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept
{
- return v.is_null();
+ return (basic_json(lhs) == rhs);
}
/*!
@@ -5668,6 +12598,8 @@ class basic_json
@complexity Linear.
+ @exceptionsafety No-throw guarantee: this function never throws exceptions.
+
@liveexample{The example demonstrates comparing several JSON
types.,operator__notequal}
@@ -5680,34 +12612,24 @@ class basic_json
/*!
@brief comparison: not equal
-
- The functions compares the given JSON value against a null pointer. As the
- null pointer can be used to initialize a JSON value to null, a comparison
- of JSON value @a v with a null pointer should be equivalent to call
- `not v.is_null()`.
-
- @param[in] v JSON value to consider
- @return whether @a v is not null
-
- @complexity Constant.
-
- @liveexample{The example compares several JSON types to the null pointer.
- ,operator__notequal__nullptr_t}
-
- @since version 1.0.0
+ @copydoc operator!=(const_reference, const_reference)
*/
- friend bool operator!=(const_reference v, std::nullptr_t) noexcept
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept
{
- return not v.is_null();
+ return (lhs != basic_json(rhs));
}
/*!
@brief comparison: not equal
- @copydoc operator!=(const_reference, std::nullptr_t)
+ @copydoc operator!=(const_reference, const_reference)
*/
- friend bool operator!=(std::nullptr_t, const_reference v) noexcept
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept
{
- return not v.is_null();
+ return (basic_json(lhs) != rhs);
}
/*!
@@ -5729,6 +12651,8 @@ class basic_json
@complexity Linear.
+ @exceptionsafety No-throw guarantee: this function never throws exceptions.
+
@liveexample{The example demonstrates comparing several JSON
types.,operator__less}
@@ -5744,41 +12668,31 @@ class basic_json
switch (lhs_type)
{
case value_t::array:
- {
- return *lhs.m_value.array < *rhs.m_value.array;
- }
+ return (*lhs.m_value.array) < (*rhs.m_value.array);
+
case value_t::object:
- {
return *lhs.m_value.object < *rhs.m_value.object;
- }
+
case value_t::null:
- {
return false;
- }
+
case value_t::string:
- {
return *lhs.m_value.string < *rhs.m_value.string;
- }
+
case value_t::boolean:
- {
return lhs.m_value.boolean < rhs.m_value.boolean;
- }
+
case value_t::number_integer:
- {
return lhs.m_value.number_integer < rhs.m_value.number_integer;
- }
+
case value_t::number_unsigned:
- {
return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned;
- }
+
case value_t::number_float:
- {
return lhs.m_value.number_float < rhs.m_value.number_float;
- }
+
default:
- {
return false;
- }
}
}
else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float)
@@ -5813,6 +12727,28 @@ class basic_json
}
/*!
+ @brief comparison: less than
+ @copydoc operator<(const_reference, const_reference)
+ */
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator<(const_reference lhs, const ScalarType rhs) noexcept
+ {
+ return (lhs < basic_json(rhs));
+ }
+
+ /*!
+ @brief comparison: less than
+ @copydoc operator<(const_reference, const_reference)
+ */
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator<(const ScalarType lhs, const_reference rhs) noexcept
+ {
+ return (basic_json(lhs) < rhs);
+ }
+
+ /*!
@brief comparison: less than or equal
Compares whether one JSON value @a lhs is less than or equal to another
@@ -5824,6 +12760,8 @@ class basic_json
@complexity Linear.
+ @exceptionsafety No-throw guarantee: this function never throws exceptions.
+
@liveexample{The example demonstrates comparing several JSON
types.,operator__greater}
@@ -5835,6 +12773,28 @@ class basic_json
}
/*!
+ @brief comparison: less than or equal
+ @copydoc operator<=(const_reference, const_reference)
+ */
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator<=(const_reference lhs, const ScalarType rhs) noexcept
+ {
+ return (lhs <= basic_json(rhs));
+ }
+
+ /*!
+ @brief comparison: less than or equal
+ @copydoc operator<=(const_reference, const_reference)
+ */
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator<=(const ScalarType lhs, const_reference rhs) noexcept
+ {
+ return (basic_json(lhs) <= rhs);
+ }
+
+ /*!
@brief comparison: greater than
Compares whether one JSON value @a lhs is greater than another
@@ -5846,6 +12806,8 @@ class basic_json
@complexity Linear.
+ @exceptionsafety No-throw guarantee: this function never throws exceptions.
+
@liveexample{The example demonstrates comparing several JSON
types.,operator__lessequal}
@@ -5857,6 +12819,28 @@ class basic_json
}
/*!
+ @brief comparison: greater than
+ @copydoc operator>(const_reference, const_reference)
+ */
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator>(const_reference lhs, const ScalarType rhs) noexcept
+ {
+ return (lhs > basic_json(rhs));
+ }
+
+ /*!
+ @brief comparison: greater than
+ @copydoc operator>(const_reference, const_reference)
+ */
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator>(const ScalarType lhs, const_reference rhs) noexcept
+ {
+ return (basic_json(lhs) > rhs);
+ }
+
+ /*!
@brief comparison: greater than or equal
Compares whether one JSON value @a lhs is greater than or equal to another
@@ -5868,6 +12852,8 @@ class basic_json
@complexity Linear.
+ @exceptionsafety No-throw guarantee: this function never throws exceptions.
+
@liveexample{The example demonstrates comparing several JSON
types.,operator__greaterequal}
@@ -5878,8 +12864,29 @@ class basic_json
return not (lhs < rhs);
}
- /// @}
+ /*!
+ @brief comparison: greater than or equal
+ @copydoc operator>=(const_reference, const_reference)
+ */
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator>=(const_reference lhs, const ScalarType rhs) noexcept
+ {
+ return (lhs >= basic_json(rhs));
+ }
+
+ /*!
+ @brief comparison: greater than or equal
+ @copydoc operator>=(const_reference, const_reference)
+ */
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator>=(const ScalarType lhs, const_reference rhs) noexcept
+ {
+ return (basic_json(lhs) >= rhs);
+ }
+ /// @}
///////////////////
// serialization //
@@ -5892,27 +12899,32 @@ class basic_json
@brief serialize to stream
Serialize the given JSON value @a j to the output stream @a o. The JSON
- value will be serialized using the @ref dump member function. The
- indentation of the output can be controlled with the member variable
- `width` of the output stream @a o. For instance, using the manipulator
- `std::setw(4)` on @a o sets the indentation level to `4` and the
- serialization result is the same as calling `dump(4)`.
+ value will be serialized using the @ref dump member function.
- @note During serializaion, the locale and the precision of the output
- stream @a o are changed. The original values are restored when the
- function returns.
+ - The indentation of the output can be controlled with the member variable
+ `width` of the output stream @a o. For instance, using the manipulator
+ `std::setw(4)` on @a o sets the indentation level to `4` and the
+ serialization result is the same as calling `dump(4)`.
+
+ - The indentation character can be controlled with the member variable
+ `fill` of the output stream @a o. For instance, the manipulator
+ `std::setfill('\\t')` sets indentation to use a tab character rather than
+ the default space character.
@param[in,out] o stream to serialize to
@param[in] j JSON value to serialize
@return the stream @a o
+ @throw type_error.316 if a string stored inside the JSON value is not
+ UTF-8 encoded
+
@complexity Linear.
@liveexample{The example below shows the serialization with different
parameters to `width` to adjust the indentation level.,operator_serialize}
- @since version 1.0.0
+ @since version 1.0.0; indentation character added in version 3.0.0
*/
friend std::ostream& operator<<(std::ostream& o, const basic_json& j)
{
@@ -5923,29 +12935,21 @@ class basic_json
// reset width to 0 for subsequent calls to this stream
o.width(0);
- // fix locale problems
- const auto old_locale = o.imbue(std::locale::classic());
- // set precision
-
- // 6, 15 or 16 digits of precision allows round-trip IEEE 754
- // string->float->string, string->double->string or string->long
- // double->string; to be safe, we read this value from
- // std::numeric_limits<number_float_t>::digits10
- const auto old_precision = o.precision(std::numeric_limits<double>::digits10);
-
// do the actual serialization
- j.dump(o, pretty_print, static_cast<unsigned int>(indentation));
-
- // reset locale and precision
- o.imbue(old_locale);
- o.precision(old_precision);
+ serializer s(detail::output_adapter<char>(o), o.fill());
+ s.dump(j, pretty_print, false, static_cast<unsigned int>(indentation));
return o;
}
/*!
@brief serialize to stream
- @copydoc operator<<(std::ostream&, const basic_json&)
+ @deprecated This stream operator is deprecated and will be removed in a
+ future version of the library. Please use
+ @ref operator<<(std::ostream&, const basic_json&)
+ instead; that is, replace calls like `j >> o;` with `o << j;`.
+ @since version 1.0.0; deprecated since version 3.0.0
*/
+ JSON_DEPRECATED
friend std::ostream& operator>>(const basic_json& j, std::ostream& o)
{
return o << j;
@@ -5962,21 +12966,47 @@ class basic_json
/// @{
/*!
- @brief deserialize from an array
+ @brief deserialize from a compatible input
- This function reads from an array of 1-byte values.
+ This function reads from a compatible input. Examples are:
+ - an array of 1-byte values
+ - strings with character/literal type with size of 1 byte
+ - input streams
+ - container with contiguous storage of 1-byte values. Compatible container
+ types include `std::vector`, `std::string`, `std::array`,
+ `std::valarray`, and `std::initializer_list`. Furthermore, C-style
+ arrays can be used with `std::begin()`/`std::end()`. User-defined
+ containers can be used as long as they implement random-access iterators
+ and a contiguous storage.
+
+ @pre Each element of the container has a size of 1 byte. Violating this
+ precondition yields undefined behavior. **This precondition is enforced
+ with a static assertion.**
+ @pre The container storage is contiguous. Violating this precondition
+ yields undefined behavior. **This precondition is enforced with an
+ assertion.**
@pre Each element of the container has a size of 1 byte. Violating this
precondition yields undefined behavior. **This precondition is enforced
with a static assertion.**
- @param[in] array array to read from
+ @warning There is no way to enforce all preconditions at compile-time. If
+ the function is called with a noncompliant container and with
+ assertions switched off, the behavior is undefined and will most
+ likely yield segmentation violation.
+
+ @param[in] i input to read from
@param[in] cb a parser callback function of type @ref parser_callback_t
which is used to control the deserialization by filtering unwanted values
(optional)
@return result of the deserialization
+ @throw parse_error.101 if a parse error occurs; example: `""unexpected end
+ of input; expected string literal""`
+ @throw parse_error.102 if to_unicode fails or surrogate error
+ @throw parse_error.103 if to_unicode fails
+
@complexity Linear in the length of the input. The parser is a predictive
LL(1) parser. The complexity can be higher if the parser callback function
@a cb has a super-linear complexity.
@@ -5986,90 +13016,46 @@ class basic_json
@liveexample{The example below demonstrates the `parse()` function reading
from an array.,parse__array__parser_callback_t}
- @since version 2.0.3
- */
- template<class T, std::size_t N>
- static basic_json parse(T (&array)[N],
- const parser_callback_t cb = nullptr)
- {
- // delegate the call to the iterator-range parse overload
- return parse(std::begin(array), std::end(array), cb);
- }
-
- /*!
- @brief deserialize from string literal
-
- @tparam CharT character/literal type with size of 1 byte
- @param[in] s string literal to read a serialized JSON value from
- @param[in] cb a parser callback function of type @ref parser_callback_t
- which is used to control the deserialization by filtering unwanted values
- (optional)
-
- @return result of the deserialization
-
- @complexity Linear in the length of the input. The parser is a predictive
- LL(1) parser. The complexity can be higher if the parser callback function
- @a cb has a super-linear complexity.
-
- @note A UTF-8 byte order mark is silently ignored.
- @note String containers like `std::string` or @ref string_t can be parsed
- with @ref parse(const ContiguousContainer&, const parser_callback_t)
-
@liveexample{The example below demonstrates the `parse()` function with
and without callback function.,parse__string__parser_callback_t}
- @sa @ref parse(std::istream&, const parser_callback_t) for a version that
- reads from an input stream
-
- @since version 1.0.0 (originally for @ref string_t)
- */
- template<typename CharT, typename std::enable_if<
- std::is_pointer<CharT>::value and
- std::is_integral<typename std::remove_pointer<CharT>::type>::value and
- sizeof(typename std::remove_pointer<CharT>::type) == 1, int>::type = 0>
- static basic_json parse(const CharT s,
- const parser_callback_t cb = nullptr)
- {
- return parser(reinterpret_cast<const char*>(s), cb).parse();
- }
-
- /*!
- @brief deserialize from stream
-
- @param[in,out] i stream to read a serialized JSON value from
- @param[in] cb a parser callback function of type @ref parser_callback_t
- which is used to control the deserialization by filtering unwanted values
- (optional)
-
- @return result of the deserialization
-
- @complexity Linear in the length of the input. The parser is a predictive
- LL(1) parser. The complexity can be higher if the parser callback function
- @a cb has a super-linear complexity.
-
- @note A UTF-8 byte order mark is silently ignored.
-
@liveexample{The example below demonstrates the `parse()` function with
and without callback function.,parse__istream__parser_callback_t}
- @sa @ref parse(const CharT, const parser_callback_t) for a version
- that reads from a string
+ @liveexample{The example below demonstrates the `parse()` function reading
+ from a contiguous container.,parse__contiguouscontainer__parser_callback_t}
- @since version 1.0.0
+ @since version 2.0.3 (contiguous containers)
*/
- static basic_json parse(std::istream& i,
- const parser_callback_t cb = nullptr)
+ static basic_json parse(detail::input_adapter i,
+ const parser_callback_t cb = nullptr,
+ const bool allow_exceptions = true)
{
- return parser(i, cb).parse();
+ basic_json result;
+ parser(i, cb, allow_exceptions).parse(true, result);
+ return result;
}
/*!
- @copydoc parse(std::istream&, const parser_callback_t)
+ @copydoc basic_json parse(detail::input_adapter, const parser_callback_t)
*/
- static basic_json parse(std::istream&& i,
- const parser_callback_t cb = nullptr)
+ static basic_json parse(detail::input_adapter& i,
+ const parser_callback_t cb = nullptr,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ parser(i, cb, allow_exceptions).parse(true, result);
+ return result;
+ }
+
+ static bool accept(detail::input_adapter i)
{
- return parser(i, cb).parse();
+ return parser(i).accept(true);
+ }
+
+ static bool accept(detail::input_adapter& i)
+ {
+ return parser(i).accept(true);
}
/*!
@@ -6099,9 +13085,15 @@ class basic_json
@param[in] cb a parser callback function of type @ref parser_callback_t
which is used to control the deserialization by filtering unwanted values
(optional)
+ @param[in] allow_exceptions whether to throw exceptions in case of a
+ parse error (optional, true by default)
@return result of the deserialization
+ @throw parse_error.101 in case of an unexpected token
+ @throw parse_error.102 if to_unicode fails or surrogate error
+ @throw parse_error.103 if to_unicode fails
+
@complexity Linear in the length of the input. The parser is a predictive
LL(1) parser. The complexity can be higher if the parser callback function
@a cb has a super-linear complexity.
@@ -6118,82 +13110,35 @@ class basic_json
std::random_access_iterator_tag,
typename std::iterator_traits<IteratorType>::iterator_category>::value, int>::type = 0>
static basic_json parse(IteratorType first, IteratorType last,
- const parser_callback_t cb = nullptr)
+ const parser_callback_t cb = nullptr,
+ const bool allow_exceptions = true)
{
- // assertion to check that the iterator range is indeed contiguous,
- // see http://stackoverflow.com/a/35008842/266378 for more discussion
- assert(std::accumulate(first, last, std::make_pair<bool, int>(true, 0),
- [&first](std::pair<bool, int> res, decltype(*first) val)
- {
- res.first &= (val == *(std::next(std::addressof(*first), res.second++)));
- return res;
- }).first);
-
- // assertion to check that each element is 1 byte long
- static_assert(sizeof(typename std::iterator_traits<IteratorType>::value_type) == 1,
- "each element in the iterator range must have the size of 1 byte");
-
- // if iterator range is empty, create a parser with an empty string
- // to generate "unexpected EOF" error message
- if (std::distance(first, last) <= 0)
- {
- return parser("").parse();
- }
+ basic_json result;
+ parser(detail::input_adapter(first, last), cb, allow_exceptions).parse(true, result);
+ return result;
+ }
- return parser(first, last, cb).parse();
+ template<class IteratorType, typename std::enable_if<
+ std::is_base_of<
+ std::random_access_iterator_tag,
+ typename std::iterator_traits<IteratorType>::iterator_category>::value, int>::type = 0>
+ static bool accept(IteratorType first, IteratorType last)
+ {
+ return parser(detail::input_adapter(first, last)).accept(true);
}
/*!
- @brief deserialize from a container with contiguous storage
-
- This function reads from a container with contiguous storage of 1-byte
- values. Compatible container types include `std::vector`, `std::string`,
- `std::array`, and `std::initializer_list`. User-defined containers can be
- used as long as they implement random-access iterators and a contiguous
- storage.
-
- @pre The container storage is contiguous. Violating this precondition
- yields undefined behavior. **This precondition is enforced with an
- assertion.**
- @pre Each element of the container has a size of 1 byte. Violating this
- precondition yields undefined behavior. **This precondition is enforced
- with a static assertion.**
-
- @warning There is no way to enforce all preconditions at compile-time. If
- the function is called with a noncompliant container and with
- assertions switched off, the behavior is undefined and will most
- likely yield segmentation violation.
-
- @tparam ContiguousContainer container type with contiguous storage
- @param[in] c container to read from
- @param[in] cb a parser callback function of type @ref parser_callback_t
- which is used to control the deserialization by filtering unwanted values
- (optional)
-
- @return result of the deserialization
-
- @complexity Linear in the length of the input. The parser is a predictive
- LL(1) parser. The complexity can be higher if the parser callback function
- @a cb has a super-linear complexity.
-
- @note A UTF-8 byte order mark is silently ignored.
-
- @liveexample{The example below demonstrates the `parse()` function reading
- from a contiguous container.,parse__contiguouscontainer__parser_callback_t}
-
- @since version 2.0.3
+ @brief deserialize from stream
+ @deprecated This stream operator is deprecated and will be removed in a
+ future version of the library. Please use
+ @ref operator>>(std::istream&, basic_json&)
+ instead; that is, replace calls like `j << i;` with `i >> j;`.
+ @since version 1.0.0; deprecated since version 3.0.0
*/
- template<class ContiguousContainer, typename std::enable_if<
- not std::is_pointer<ContiguousContainer>::value and
- std::is_base_of<
- std::random_access_iterator_tag,
- typename std::iterator_traits<decltype(std::begin(std::declval<ContiguousContainer const>()))>::iterator_category>::value
- , int>::type = 0>
- static basic_json parse(const ContiguousContainer& c,
- const parser_callback_t cb = nullptr)
+ JSON_DEPRECATED
+ friend std::istream& operator<<(basic_json& j, std::istream& i)
{
- // delegate the call to the iterator-range parse overload
- return parse(std::begin(c), std::end(c), cb);
+ return operator>>(i, j);
}
/*!
@@ -6204,7 +13149,9 @@ class basic_json
@param[in,out] i input stream to read a serialized JSON value from
@param[in,out] j JSON value to write the deserialized input to
- @throw std::invalid_argument in case of parse errors
+ @throw parse_error.101 in case of an unexpected token
+ @throw parse_error.102 if to_unicode fails or surrogate error
+ @throw parse_error.103 if to_unicode fails
@complexity Linear in the length of the input. The parser is a predictive
LL(1) parser.
@@ -6219,1401 +13166,194 @@ class basic_json
@since version 1.0.0
*/
- friend std::istream& operator<<(basic_json& j, std::istream& i)
- {
- j = parser(i).parse();
- return i;
- }
-
- /*!
- @brief deserialize from stream
- @copydoc operator<<(basic_json&, std::istream&)
- */
friend std::istream& operator>>(std::istream& i, basic_json& j)
{
- j = parser(i).parse();
+ parser(detail::input_adapter(i)).parse(false, j);
return i;
}
/// @}
- //////////////////////////////////////////
- // binary serialization/deserialization //
- //////////////////////////////////////////
-
- /// @name binary serialization/deserialization support
- /// @{
-
- private:
- template<typename T>
- static void add_to_vector(std::vector<uint8_t>& vec, size_t bytes, const T number)
- {
- assert(bytes == 1 or bytes == 2 or bytes == 4 or bytes == 8);
-
- switch (bytes)
- {
- case 8:
- {
- vec.push_back(static_cast<uint8_t>((number >> 070) & 0xff));
- vec.push_back(static_cast<uint8_t>((number >> 060) & 0xff));
- vec.push_back(static_cast<uint8_t>((number >> 050) & 0xff));
- vec.push_back(static_cast<uint8_t>((number >> 040) & 0xff));
- // intentional fall-through
- }
-
- case 4:
- {
- vec.push_back(static_cast<uint8_t>((number >> 030) & 0xff));
- vec.push_back(static_cast<uint8_t>((number >> 020) & 0xff));
- // intentional fall-through
- }
-
- case 2:
- {
- vec.push_back(static_cast<uint8_t>((number >> 010) & 0xff));
- // intentional fall-through
- }
-
- case 1:
- {
- vec.push_back(static_cast<uint8_t>(number & 0xff));
- break;
- }
- }
- }
+ ///////////////////////////
+ // convenience functions //
+ ///////////////////////////
/*!
- @brief take sufficient bytes from a vector to fill an integer variable
-
- In the context of binary serialization formats, we need to read several
- bytes from a byte vector and combine them to multi-byte integral data
- types.
-
- @param[in] vec byte vector to read from
- @param[in] current_index the position in the vector after which to read
-
- @return the next sizeof(T) bytes from @a vec, in reverse order as T
-
- @tparam T the integral return type
-
- @throw std::out_of_range if there are less than sizeof(T)+1 bytes in the
- vector @a vec to read
-
- In the for loop, the bytes from the vector are copied in reverse order into
- the return value. In the figures below, let sizeof(T)=4 and `i` be the loop
- variable.
-
- Precondition:
-
- vec: | | | a | b | c | d | T: | | | | |
- ^ ^ ^ ^
- current_index i ptr sizeof(T)
-
- Postcondition:
+ @brief return the type as string
- vec: | | | a | b | c | d | T: | d | c | b | a |
- ^ ^ ^
- | i ptr
- current_index
+ Returns the type name as string to be used in error messages - usually to
+ indicate that a function was called on a wrong JSON type.
- @sa Code adapted from <http://stackoverflow.com/a/41031865/266378>.
- */
- template<typename T>
- static T get_from_vector(const std::vector<uint8_t>& vec, const size_t current_index)
- {
- if (current_index + sizeof(T) + 1 > vec.size())
- {
- throw std::out_of_range("cannot read " + std::to_string(sizeof(T)) + " bytes from vector");
- }
+ @return a string representation of a the @a m_type member:
+ Value type | return value
+ ----------- | -------------
+ null | `"null"`
+ boolean | `"boolean"`
+ string | `"string"`
+ number | `"number"` (for all number types)
+ object | `"object"`
+ array | `"array"`
+ discarded | `"discarded"`
- T result;
- uint8_t* ptr = reinterpret_cast<uint8_t*>(&result);
- for (size_t i = 0; i < sizeof(T); ++i)
- {
- *ptr++ = vec[current_index + sizeof(T) - i];
- }
- return result;
- }
+ @exceptionsafety No-throw guarantee: this function never throws exceptions.
- /*!
- @brief create a MessagePack serialization of a given JSON value
+ @complexity Constant.
- This is a straightforward implementation of the MessagePack specification.
+ @liveexample{The following code exemplifies `type_name()` for all JSON
+ types.,type_name}
- @param[in] j JSON value to serialize
- @param[in,out] v byte vector to write the serialization to
+ @sa @ref type() -- return the type of the JSON value
+ @sa @ref operator value_t() -- return the type of the JSON value (implicit)
- @sa https://github.com/msgpack/msgpack/blob/master/spec.md
+ @since version 1.0.0, public since 2.1.0, `const char*` and `noexcept`
+ since 3.0.0
*/
- static void to_msgpack_internal(const basic_json& j, std::vector<uint8_t>& v)
+ const char* type_name() const noexcept
{
- switch (j.type())
{
- case value_t::null:
- {
- // nil
- v.push_back(0xc0);
- break;
- }
-
- case value_t::boolean:
- {
- // true and false
- v.push_back(j.m_value.boolean ? 0xc3 : 0xc2);
- break;
- }
-
- case value_t::number_integer:
- {
- if (j.m_value.number_integer >= 0)
- {
- // MessagePack does not differentiate between positive
- // signed integers and unsigned integers. Therefore, we used
- // the code from the value_t::number_unsigned case here.
- if (j.m_value.number_unsigned < 128)
- {
- // positive fixnum
- add_to_vector(v, 1, j.m_value.number_unsigned);
- }
- else if (j.m_value.number_unsigned <= UINT8_MAX)
- {
- // uint 8
- v.push_back(0xcc);
- add_to_vector(v, 1, j.m_value.number_unsigned);
- }
- else if (j.m_value.number_unsigned <= UINT16_MAX)
- {
- // uint 16
- v.push_back(0xcd);
- add_to_vector(v, 2, j.m_value.number_unsigned);
- }
- else if (j.m_value.number_unsigned <= UINT32_MAX)
- {
- // uint 32
- v.push_back(0xce);
- add_to_vector(v, 4, j.m_value.number_unsigned);
- }
- else if (j.m_value.number_unsigned <= UINT64_MAX)
- {
- // uint 64
- v.push_back(0xcf);
- add_to_vector(v, 8, j.m_value.number_unsigned);
- }
- }
- else
- {
- if (j.m_value.number_integer >= -32)
- {
- // negative fixnum
- add_to_vector(v, 1, j.m_value.number_integer);
- }
- else if (j.m_value.number_integer >= INT8_MIN and j.m_value.number_integer <= INT8_MAX)
- {
- // int 8
- v.push_back(0xd0);
- add_to_vector(v, 1, j.m_value.number_integer);
- }
- else if (j.m_value.number_integer >= INT16_MIN and j.m_value.number_integer <= INT16_MAX)
- {
- // int 16
- v.push_back(0xd1);
- add_to_vector(v, 2, j.m_value.number_integer);
- }
- else if (j.m_value.number_integer >= INT32_MIN and j.m_value.number_integer <= INT32_MAX)
- {
- // int 32
- v.push_back(0xd2);
- add_to_vector(v, 4, j.m_value.number_integer);
- }
- else if (j.m_value.number_integer >= INT64_MIN and j.m_value.number_integer <= INT64_MAX)
- {
- // int 64
- v.push_back(0xd3);
- add_to_vector(v, 8, j.m_value.number_integer);
- }
- }
- break;
- }
-
- case value_t::number_unsigned:
- {
- if (j.m_value.number_unsigned < 128)
- {
- // positive fixnum
- add_to_vector(v, 1, j.m_value.number_unsigned);
- }
- else if (j.m_value.number_unsigned <= UINT8_MAX)
- {
- // uint 8
- v.push_back(0xcc);
- add_to_vector(v, 1, j.m_value.number_unsigned);
- }
- else if (j.m_value.number_unsigned <= UINT16_MAX)
- {
- // uint 16
- v.push_back(0xcd);
- add_to_vector(v, 2, j.m_value.number_unsigned);
- }
- else if (j.m_value.number_unsigned <= UINT32_MAX)
- {
- // uint 32
- v.push_back(0xce);
- add_to_vector(v, 4, j.m_value.number_unsigned);
- }
- else if (j.m_value.number_unsigned <= UINT64_MAX)
- {
- // uint 64
- v.push_back(0xcf);
- add_to_vector(v, 8, j.m_value.number_unsigned);
- }
- break;
- }
-
- case value_t::number_float:
- {
- // float 64
- v.push_back(0xcb);
- const uint8_t* helper = reinterpret_cast<const uint8_t*>(&(j.m_value.number_float));
- for (size_t i = 0; i < 8; ++i)
- {
- v.push_back(helper[7 - i]);
- }
- break;
- }
-
- case value_t::string:
- {
- const auto N = j.m_value.string->size();
- if (N <= 31)
- {
- // fixstr
- v.push_back(static_cast<uint8_t>(0xa0 | N));
- }
- else if (N <= 255)
- {
- // str 8
- v.push_back(0xd9);
- add_to_vector(v, 1, N);
- }
- else if (N <= 65535)
- {
- // str 16
- v.push_back(0xda);
- add_to_vector(v, 2, N);
- }
- else if (N <= 4294967295)
- {
- // str 32
- v.push_back(0xdb);
- add_to_vector(v, 4, N);
- }
-
- // append string
- std::copy(j.m_value.string->begin(), j.m_value.string->end(),
- std::back_inserter(v));
- break;
- }
-
- case value_t::array:
+ switch (m_type)
{
- const auto N = j.m_value.array->size();
- if (N <= 15)
- {
- // fixarray
- v.push_back(static_cast<uint8_t>(0x90 | N));
- }
- else if (N <= 0xffff)
- {
- // array 16
- v.push_back(0xdc);
- add_to_vector(v, 2, N);
- }
- else if (N <= 0xffffffff)
- {
- // array 32
- v.push_back(0xdd);
- add_to_vector(v, 4, N);
- }
-
- // append each element
- for (const auto& el : *j.m_value.array)
- {
- to_msgpack_internal(el, v);
- }
- break;
- }
-
- case value_t::object:
- {
- const auto N = j.m_value.object->size();
- if (N <= 15)
- {
- // fixmap
- v.push_back(static_cast<uint8_t>(0x80 | (N & 0xf)));
- }
- else if (N <= 65535)
- {
- // map 16
- v.push_back(0xde);
- add_to_vector(v, 2, N);
- }
- else if (N <= 4294967295)
- {
- // map 32
- v.push_back(0xdf);
- add_to_vector(v, 4, N);
- }
-
- // append each element
- for (const auto& el : *j.m_value.object)
- {
- to_msgpack_internal(el.first, v);
- to_msgpack_internal(el.second, v);
- }
- break;
- }
-
- default:
- {
- break;
+ case value_t::null:
+ return "null";
+ case value_t::object:
+ return "object";
+ case value_t::array:
+ return "array";
+ case value_t::string:
+ return "string";
+ case value_t::boolean:
+ return "boolean";
+ case value_t::discarded:
+ return "discarded";
+ default:
+ return "number";
}
}
}
- /*!
- @brief create a CBOR serialization of a given JSON value
-
- This is a straightforward implementation of the CBOR specification.
-
- @param[in] j JSON value to serialize
- @param[in,out] v byte vector to write the serialization to
-
- @sa https://tools.ietf.org/html/rfc7049
- */
- static void to_cbor_internal(const basic_json& j, std::vector<uint8_t>& v)
- {
- switch (j.type())
- {
- case value_t::null:
- {
- v.push_back(0xf6);
- break;
- }
-
- case value_t::boolean:
- {
- v.push_back(j.m_value.boolean ? 0xf5 : 0xf4);
- break;
- }
-
- case value_t::number_integer:
- {
- if (j.m_value.number_integer >= 0)
- {
- // CBOR does not differentiate between positive signed
- // integers and unsigned integers. Therefore, we used the
- // code from the value_t::number_unsigned case here.
- if (j.m_value.number_integer <= 0x17)
- {
- add_to_vector(v, 1, j.m_value.number_integer);
- }
- else if (j.m_value.number_integer <= UINT8_MAX)
- {
- v.push_back(0x18);
- // one-byte uint8_t
- add_to_vector(v, 1, j.m_value.number_integer);
- }
- else if (j.m_value.number_integer <= UINT16_MAX)
- {
- v.push_back(0x19);
- // two-byte uint16_t
- add_to_vector(v, 2, j.m_value.number_integer);
- }
- else if (j.m_value.number_integer <= UINT32_MAX)
- {
- v.push_back(0x1a);
- // four-byte uint32_t
- add_to_vector(v, 4, j.m_value.number_integer);
- }
- else
- {
- v.push_back(0x1b);
- // eight-byte uint64_t
- add_to_vector(v, 8, j.m_value.number_integer);
- }
- }
- else
- {
- // The conversions below encode the sign in the first byte,
- // and the value is converted to a positive number.
- const auto positive_number = -1 - j.m_value.number_integer;
- if (j.m_value.number_integer >= -24)
- {
- v.push_back(static_cast<uint8_t>(0x20 + positive_number));
- }
- else if (positive_number <= UINT8_MAX)
- {
- // int 8
- v.push_back(0x38);
- add_to_vector(v, 1, positive_number);
- }
- else if (positive_number <= UINT16_MAX)
- {
- // int 16
- v.push_back(0x39);
- add_to_vector(v, 2, positive_number);
- }
- else if (positive_number <= UINT32_MAX)
- {
- // int 32
- v.push_back(0x3a);
- add_to_vector(v, 4, positive_number);
- }
- else
- {
- // int 64
- v.push_back(0x3b);
- add_to_vector(v, 8, positive_number);
- }
- }
- break;
- }
-
- case value_t::number_unsigned:
- {
- if (j.m_value.number_unsigned <= 0x17)
- {
- v.push_back(static_cast<uint8_t>(j.m_value.number_unsigned));
- }
- else if (j.m_value.number_unsigned <= 0xff)
- {
- v.push_back(0x18);
- // one-byte uint8_t
- add_to_vector(v, 1, j.m_value.number_unsigned);
- }
- else if (j.m_value.number_unsigned <= 0xffff)
- {
- v.push_back(0x19);
- // two-byte uint16_t
- add_to_vector(v, 2, j.m_value.number_unsigned);
- }
- else if (j.m_value.number_unsigned <= 0xffffffff)
- {
- v.push_back(0x1a);
- // four-byte uint32_t
- add_to_vector(v, 4, j.m_value.number_unsigned);
- }
- else if (j.m_value.number_unsigned <= 0xffffffffffffffff)
- {
- v.push_back(0x1b);
- // eight-byte uint64_t
- add_to_vector(v, 8, j.m_value.number_unsigned);
- }
- break;
- }
-
- case value_t::number_float:
- {
- // Double-Precision Float
- v.push_back(0xfb);
- const uint8_t* helper = reinterpret_cast<const uint8_t*>(&(j.m_value.number_float));
- for (size_t i = 0; i < 8; ++i)
- {
- v.push_back(helper[7 - i]);
- }
- break;
- }
- case value_t::string:
- {
- const auto N = j.m_value.string->size();
- if (N <= 0x17)
- {
- v.push_back(0x60 + N); // 1 byte for string + size
- }
- else if (N <= 0xff)
- {
- v.push_back(0x78); // one-byte uint8_t for N
- add_to_vector(v, 1, N);
- }
- else if (N <= 0xffff)
- {
- v.push_back(0x79); // two-byte uint16_t for N
- add_to_vector(v, 2, N);
- }
- else if (N <= 0xffffffff)
- {
- v.push_back(0x7a); // four-byte uint32_t for N
- add_to_vector(v, 4, N);
- }
- // LCOV_EXCL_START
- else if (N <= 0xffffffffffffffff)
- {
- v.push_back(0x7b); // eight-byte uint64_t for N
- add_to_vector(v, 8, N);
- }
- // LCOV_EXCL_STOP
-
- // append string
- std::copy(j.m_value.string->begin(), j.m_value.string->end(),
- std::back_inserter(v));
- break;
- }
+ private:
+ //////////////////////
+ // member variables //
+ //////////////////////
- case value_t::array:
- {
- const auto N = j.m_value.array->size();
- if (N <= 0x17)
- {
- v.push_back(0x80 + N); // 1 byte for array + size
- }
- else if (N <= 0xff)
- {
- v.push_back(0x98); // one-byte uint8_t for N
- add_to_vector(v, 1, N);
- }
- else if (N <= 0xffff)
- {
- v.push_back(0x99); // two-byte uint16_t for N
- add_to_vector(v, 2, N);
- }
- else if (N <= 0xffffffff)
- {
- v.push_back(0x9a); // four-byte uint32_t for N
- add_to_vector(v, 4, N);
- }
- // LCOV_EXCL_START
- else if (N <= 0xffffffffffffffff)
- {
- v.push_back(0x9b); // eight-byte uint64_t for N
- add_to_vector(v, 8, N);
- }
- // LCOV_EXCL_STOP
+ /// the type of the current element
+ value_t m_type = value_t::null;
- // append each element
- for (const auto& el : *j.m_value.array)
- {
- to_cbor_internal(el, v);
- }
- break;
- }
+ /// the value of the current element
+ json_value m_value = {};
- case value_t::object:
- {
- const auto N = j.m_value.object->size();
- if (N <= 0x17)
- {
- v.push_back(0xa0 + N); // 1 byte for object + size
- }
- else if (N <= 0xff)
- {
- v.push_back(0xb8);
- add_to_vector(v, 1, N); // one-byte uint8_t for N
- }
- else if (N <= 0xffff)
- {
- v.push_back(0xb9);
- add_to_vector(v, 2, N); // two-byte uint16_t for N
- }
- else if (N <= 0xffffffff)
- {
- v.push_back(0xba);
- add_to_vector(v, 4, N); // four-byte uint32_t for N
- }
- // LCOV_EXCL_START
- else if (N <= 0xffffffffffffffff)
- {
- v.push_back(0xbb);
- add_to_vector(v, 8, N); // eight-byte uint64_t for N
- }
- // LCOV_EXCL_STOP
+ //////////////////////////////////////////
+ // binary serialization/deserialization //
+ //////////////////////////////////////////
- // append each element
- for (const auto& el : *j.m_value.object)
- {
- to_cbor_internal(el.first, v);
- to_cbor_internal(el.second, v);
- }
- break;
- }
+ /// @name binary serialization/deserialization support
+ /// @{
- default:
- {
- break;
- }
- }
- }
+ public:
+ /*!
+ @brief create a CBOR serialization of a given JSON value
+ Serializes a given JSON value @a j to a byte vector using the CBOR (Concise
+ Binary Object Representation) serialization format. CBOR is a binary
+ serialization format which aims to be more compact than JSON itself, yet
+ more efficient to parse.
- /*
- @brief checks if given lengths do not exceed the size of a given vector
+ The library uses the following mapping from JSON values types to
+ CBOR types according to the CBOR specification (RFC 7049):
+
+ JSON value type | value/range | CBOR type | first byte
+ --------------- | ------------------------------------------ | ---------------------------------- | ---------------
+ null | `null` | Null | 0xF6
+ boolean | `true` | True | 0xF5
+ boolean | `false` | False | 0xF4
+ number_integer | -9223372036854775808..-2147483649 | Negative integer (8 bytes follow) | 0x3B
+ number_integer | -2147483648..-32769 | Negative integer (4 bytes follow) | 0x3A
+ number_integer | -32768..-129 | Negative integer (2 bytes follow) | 0x39
+ number_integer | -128..-25 | Negative integer (1 byte follow) | 0x38
+ number_integer | -24..-1 | Negative integer | 0x20..0x37
+ number_integer | 0..23 | Integer | 0x00..0x17
+ number_integer | 24..255 | Unsigned integer (1 byte follow) | 0x18
+ number_integer | 256..65535 | Unsigned integer (2 bytes follow) | 0x19
+ number_integer | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A
+ number_integer | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B
+ number_unsigned | 0..23 | Integer | 0x00..0x17
+ number_unsigned | 24..255 | Unsigned integer (1 byte follow) | 0x18
+ number_unsigned | 256..65535 | Unsigned integer (2 bytes follow) | 0x19
+ number_unsigned | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A
+ number_unsigned | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B
+ number_float | *any value* | Double-Precision Float | 0xFB
+ string | *length*: 0..23 | UTF-8 string | 0x60..0x77
+ string | *length*: 23..255 | UTF-8 string (1 byte follow) | 0x78
+ string | *length*: 256..65535 | UTF-8 string (2 bytes follow) | 0x79
+ string | *length*: 65536..4294967295 | UTF-8 string (4 bytes follow) | 0x7A
+ string | *length*: 4294967296..18446744073709551615 | UTF-8 string (8 bytes follow) | 0x7B
+ array | *size*: 0..23 | array | 0x80..0x97
+ array | *size*: 23..255 | array (1 byte follow) | 0x98
+ array | *size*: 256..65535 | array (2 bytes follow) | 0x99
+ array | *size*: 65536..4294967295 | array (4 bytes follow) | 0x9A
+ array | *size*: 4294967296..18446744073709551615 | array (8 bytes follow) | 0x9B
+ object | *size*: 0..23 | map | 0xA0..0xB7
+ object | *size*: 23..255 | map (1 byte follow) | 0xB8
+ object | *size*: 256..65535 | map (2 bytes follow) | 0xB9
+ object | *size*: 65536..4294967295 | map (4 bytes follow) | 0xBA
+ object | *size*: 4294967296..18446744073709551615 | map (8 bytes follow) | 0xBB
+
+ @note The mapping is **complete** in the sense that any JSON value type
+ can be converted to a CBOR value.
+
+ @note If NaN or Infinity are stored inside a JSON number, they are
+ serialized properly. This behavior differs from the @ref dump()
+ function which serializes NaN or Infinity to `null`.
+
+ @note The following CBOR types are not used in the conversion:
+ - byte strings (0x40..0x5F)
+ - UTF-8 strings terminated by "break" (0x7F)
+ - arrays terminated by "break" (0x9F)
+ - maps terminated by "break" (0xBF)
+ - date/time (0xC0..0xC1)
+ - bignum (0xC2..0xC3)
+ - decimal fraction (0xC4)
+ - bigfloat (0xC5)
+ - tagged items (0xC6..0xD4, 0xD8..0xDB)
+ - expected conversions (0xD5..0xD7)
+ - simple values (0xE0..0xF3, 0xF8)
+ - undefined (0xF7)
+ - half and single-precision floats (0xF9-0xFA)
+ - break (0xFF)
- To secure the access to the byte vector during CBOR/MessagePack
- deserialization, bytes are copied from the vector into buffers. This
- function checks if the number of bytes to copy (@a len) does not exceed the
- size @s size of the vector. Additionally, an @a offset is given from where
- to start reading the bytes.
+ @param[in] j JSON value to serialize
+ @return MessagePack serialization as byte vector
- This function checks whether reading the bytes is safe; that is, offset is a
- valid index in the vector, offset+len
+ @complexity Linear in the size of the JSON value @a j.
- @param[in] size size of the byte vector
- @param[in] len number of bytes to read
- @param[in] offset offset where to start reading
+ @liveexample{The example shows the serialization of a JSON value to a byte
+ vector in CBOR format.,to_cbor}
- vec: x x x x x X X X X X
- ^ ^ ^
- 0 offset len
+ @sa http://cbor.io
+ @sa @ref from_cbor(const std::vector<uint8_t>&, const size_t) for the
+ analogous deserialization
+ @sa @ref to_msgpack(const basic_json&) for the related MessagePack format
- @throws out_of_range if `len > v.size()`
+ @since version 2.0.9
*/
- static void check_length(const size_t size, const size_t len, const size_t offset)
+ static std::vector<uint8_t> to_cbor(const basic_json& j)
{
- // simple case: requested length is greater than the vector's length
- if (len > size or offset > size)
- {
- throw std::out_of_range("len out of range");
- }
-
- // second case: adding offset would result in overflow
- if ((size > (std::numeric_limits<size_t>::max() - offset)))
- {
- throw std::out_of_range("len+offset out of range");
- }
-
- // last case: reading past the end of the vector
- if (len + offset > size)
- {
- throw std::out_of_range("len+offset out of range");
- }
+ std::vector<uint8_t> result;
+ to_cbor(j, result);
+ return result;
}
- /*!
- @brief create a JSON value from a given MessagePack vector
-
- @param[in] v MessagePack serialization
- @param[in] idx byte index to start reading from @a v
-
- @return deserialized JSON value
-
- @throw std::invalid_argument if unsupported features from MessagePack were
- used in the given vector @a v or if the input is not valid MessagePack
- @throw std::out_of_range if the given vector ends prematurely
-
- @sa https://github.com/msgpack/msgpack/blob/master/spec.md
- */
- static basic_json from_msgpack_internal(const std::vector<uint8_t>& v, size_t& idx)
+ static void to_cbor(const basic_json& j, detail::output_adapter<uint8_t> o)
{
- // make sure reading 1 byte is safe
- check_length(v.size(), 1, idx);
-
- // store and increment index
- const size_t current_idx = idx++;
-
- if (v[current_idx] <= 0xbf)
- {
- if (v[current_idx] <= 0x7f) // positive fixint
- {
- return v[current_idx];
- }
- else if (v[current_idx] <= 0x8f) // fixmap
- {
- basic_json result = value_t::object;
- const size_t len = v[current_idx] & 0x0f;
- for (size_t i = 0; i < len; ++i)
- {
- std::string key = from_msgpack_internal(v, idx);
- result[key] = from_msgpack_internal(v, idx);
- }
- return result;
- }
- else if (v[current_idx] <= 0x9f) // fixarray
- {
- basic_json result = value_t::array;
- const size_t len = v[current_idx] & 0x0f;
- for (size_t i = 0; i < len; ++i)
- {
- result.push_back(from_msgpack_internal(v, idx));
- }
- return result;
- }
- else // fixstr
- {
- const size_t len = v[current_idx] & 0x1f;
- const size_t offset = current_idx + 1;
- idx += len; // skip content bytes
- check_length(v.size(), len, offset);
- return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
- }
- }
- else if (v[current_idx] >= 0xe0) // negative fixint
- {
- return static_cast<int8_t>(v[current_idx]);
- }
- else
- {
- switch (v[current_idx])
- {
- case 0xc0: // nil
- {
- return value_t::null;
- }
-
- case 0xc2: // false
- {
- return false;
- }
-
- case 0xc3: // true
- {
- return true;
- }
-
- case 0xca: // float 32
- {
- // copy bytes in reverse order into the double variable
- check_length(v.size(), sizeof(float), 1);
- float res;
- for (size_t byte = 0; byte < sizeof(float); ++byte)
- {
- reinterpret_cast<uint8_t*>(&res)[sizeof(float) - byte - 1] = v[current_idx + 1 + byte];
- }
- idx += sizeof(float); // skip content bytes
- return res;
- }
-
- case 0xcb: // float 64
- {
- // copy bytes in reverse order into the double variable
- check_length(v.size(), sizeof(double), 1);
- double res;
- for (size_t byte = 0; byte < sizeof(double); ++byte)
- {
- reinterpret_cast<uint8_t*>(&res)[sizeof(double) - byte - 1] = v[current_idx + 1 + byte];
- }
- idx += sizeof(double); // skip content bytes
- return res;
- }
-
- case 0xcc: // uint 8
- {
- idx += 1; // skip content byte
- return get_from_vector<uint8_t>(v, current_idx);
- }
-
- case 0xcd: // uint 16
- {
- idx += 2; // skip 2 content bytes
- return get_from_vector<uint16_t>(v, current_idx);
- }
-
- case 0xce: // uint 32
- {
- idx += 4; // skip 4 content bytes
- return get_from_vector<uint32_t>(v, current_idx);
- }
-
- case 0xcf: // uint 64
- {
- idx += 8; // skip 8 content bytes
- return get_from_vector<uint64_t>(v, current_idx);
- }
-
- case 0xd0: // int 8
- {
- idx += 1; // skip content byte
- return get_from_vector<int8_t>(v, current_idx);
- }
-
- case 0xd1: // int 16
- {
- idx += 2; // skip 2 content bytes
- return get_from_vector<int16_t>(v, current_idx);
- }
-
- case 0xd2: // int 32
- {
- idx += 4; // skip 4 content bytes
- return get_from_vector<int32_t>(v, current_idx);
- }
-
- case 0xd3: // int 64
- {
- idx += 8; // skip 8 content bytes
- return get_from_vector<int64_t>(v, current_idx);
- }
-
- case 0xd9: // str 8
- {
- const auto len = static_cast<size_t>(get_from_vector<uint8_t>(v, current_idx));
- const size_t offset = current_idx + 2;
- idx += len + 1; // skip size byte + content bytes
- check_length(v.size(), len, offset);
- return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
- }
-
- case 0xda: // str 16
- {
- const auto len = static_cast<size_t>(get_from_vector<uint16_t>(v, current_idx));
- const size_t offset = current_idx + 3;
- idx += len + 2; // skip 2 size bytes + content bytes
- check_length(v.size(), len, offset);
- return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
- }
-
- case 0xdb: // str 32
- {
- const auto len = static_cast<size_t>(get_from_vector<uint32_t>(v, current_idx));
- const size_t offset = current_idx + 5;
- idx += len + 4; // skip 4 size bytes + content bytes
- check_length(v.size(), len, offset);
- return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
- }
-
- case 0xdc: // array 16
- {
- basic_json result = value_t::array;
- const auto len = static_cast<size_t>(get_from_vector<uint16_t>(v, current_idx));
- idx += 2; // skip 2 size bytes
- for (size_t i = 0; i < len; ++i)
- {
- result.push_back(from_msgpack_internal(v, idx));
- }
- return result;
- }
-
- case 0xdd: // array 32
- {
- basic_json result = value_t::array;
- const auto len = static_cast<size_t>(get_from_vector<uint32_t>(v, current_idx));
- idx += 4; // skip 4 size bytes
- for (size_t i = 0; i < len; ++i)
- {
- result.push_back(from_msgpack_internal(v, idx));
- }
- return result;
- }
-
- case 0xde: // map 16
- {
- basic_json result = value_t::object;
- const auto len = static_cast<size_t>(get_from_vector<uint16_t>(v, current_idx));
- idx += 2; // skip 2 size bytes
- for (size_t i = 0; i < len; ++i)
- {
- std::string key = from_msgpack_internal(v, idx);
- result[key] = from_msgpack_internal(v, idx);
- }
- return result;
- }
-
- case 0xdf: // map 32
- {
- basic_json result = value_t::object;
- const auto len = static_cast<size_t>(get_from_vector<uint32_t>(v, current_idx));
- idx += 4; // skip 4 size bytes
- for (size_t i = 0; i < len; ++i)
- {
- std::string key = from_msgpack_internal(v, idx);
- result[key] = from_msgpack_internal(v, idx);
- }
- return result;
- }
-
- default:
- {
- throw std::invalid_argument("error parsing a msgpack @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast<int>(v[current_idx])));
- }
- }
- }
+ binary_writer<uint8_t>(o).write_cbor(j);
}
- /*!
- @brief create a JSON value from a given CBOR vector
-
- @param[in] v CBOR serialization
- @param[in] idx byte index to start reading from @a v
-
- @return deserialized JSON value
-
- @throw std::invalid_argument if unsupported features from CBOR were used in
- the given vector @a v or if the input is not valid CBOR
- @throw std::out_of_range if the given vector ends prematurely
-
- @sa https://tools.ietf.org/html/rfc7049
- */
- static basic_json from_cbor_internal(const std::vector<uint8_t>& v, size_t& idx)
+ static void to_cbor(const basic_json& j, detail::output_adapter<char> o)
{
- // store and increment index
- const size_t current_idx = idx++;
-
- switch (v.at(current_idx))
- {
- // Integer 0x00..0x17 (0..23)
- case 0x00:
- case 0x01:
- case 0x02:
- case 0x03:
- case 0x04:
- case 0x05:
- case 0x06:
- case 0x07:
- case 0x08:
- case 0x09:
- case 0x0a:
- case 0x0b:
- case 0x0c:
- case 0x0d:
- case 0x0e:
- case 0x0f:
- case 0x10:
- case 0x11:
- case 0x12:
- case 0x13:
- case 0x14:
- case 0x15:
- case 0x16:
- case 0x17:
- {
- return v[current_idx];
- }
-
- case 0x18: // Unsigned integer (one-byte uint8_t follows)
- {
- idx += 1; // skip content byte
- return get_from_vector<uint8_t>(v, current_idx);
- }
-
- case 0x19: // Unsigned integer (two-byte uint16_t follows)
- {
- idx += 2; // skip 2 content bytes
- return get_from_vector<uint16_t>(v, current_idx);
- }
-
- case 0x1a: // Unsigned integer (four-byte uint32_t follows)
- {
- idx += 4; // skip 4 content bytes
- return get_from_vector<uint32_t>(v, current_idx);
- }
-
- case 0x1b: // Unsigned integer (eight-byte uint64_t follows)
- {
- idx += 8; // skip 8 content bytes
- return get_from_vector<uint64_t>(v, current_idx);
- }
-
- // Negative integer -1-0x00..-1-0x17 (-1..-24)
- case 0x20:
- case 0x21:
- case 0x22:
- case 0x23:
- case 0x24:
- case 0x25:
- case 0x26:
- case 0x27:
- case 0x28:
- case 0x29:
- case 0x2a:
- case 0x2b:
- case 0x2c:
- case 0x2d:
- case 0x2e:
- case 0x2f:
- case 0x30:
- case 0x31:
- case 0x32:
- case 0x33:
- case 0x34:
- case 0x35:
- case 0x36:
- case 0x37:
- {
- return static_cast<int8_t>(0x20 - 1 - v[current_idx]);
- }
-
- case 0x38: // Negative integer (one-byte uint8_t follows)
- {
- idx += 1; // skip content byte
- // must be uint8_t !
- return static_cast<number_integer_t>(-1) - get_from_vector<uint8_t>(v, current_idx);
- }
-
- case 0x39: // Negative integer -1-n (two-byte uint16_t follows)
- {
- idx += 2; // skip 2 content bytes
- return static_cast<number_integer_t>(-1) - get_from_vector<uint16_t>(v, current_idx);
- }
-
- case 0x3a: // Negative integer -1-n (four-byte uint32_t follows)
- {
- idx += 4; // skip 4 content bytes
- return static_cast<number_integer_t>(-1) - get_from_vector<uint32_t>(v, current_idx);
- }
-
- case 0x3b: // Negative integer -1-n (eight-byte uint64_t follows)
- {
- idx += 8; // skip 8 content bytes
- return static_cast<number_integer_t>(-1) - static_cast<number_integer_t>(get_from_vector<uint64_t>(v, current_idx));
- }
-
- // UTF-8 string (0x00..0x17 bytes follow)
- case 0x60:
- case 0x61:
- case 0x62:
- case 0x63:
- case 0x64:
- case 0x65:
- case 0x66:
- case 0x67:
- case 0x68:
- case 0x69:
- case 0x6a:
- case 0x6b:
- case 0x6c:
- case 0x6d:
- case 0x6e:
- case 0x6f:
- case 0x70:
- case 0x71:
- case 0x72:
- case 0x73:
- case 0x74:
- case 0x75:
- case 0x76:
- case 0x77:
- {
- const auto len = static_cast<size_t>(v[current_idx] - 0x60);
- const size_t offset = current_idx + 1;
- idx += len; // skip content bytes
- check_length(v.size(), len, offset);
- return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
- }
-
- case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
- {
- const auto len = static_cast<size_t>(get_from_vector<uint8_t>(v, current_idx));
- const size_t offset = current_idx + 2;
- idx += len + 1; // skip size byte + content bytes
- check_length(v.size(), len, offset);
- return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
- }
-
- case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
- {
- const auto len = static_cast<size_t>(get_from_vector<uint16_t>(v, current_idx));
- const size_t offset = current_idx + 3;
- idx += len + 2; // skip 2 size bytes + content bytes
- check_length(v.size(), len, offset);
- return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
- }
-
- case 0x7a: // UTF-8 string (four-byte uint32_t for n follow)
- {
- const auto len = static_cast<size_t>(get_from_vector<uint32_t>(v, current_idx));
- const size_t offset = current_idx + 5;
- idx += len + 4; // skip 4 size bytes + content bytes
- check_length(v.size(), len, offset);
- return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
- }
-
- case 0x7b: // UTF-8 string (eight-byte uint64_t for n follow)
- {
- const auto len = static_cast<size_t>(get_from_vector<uint64_t>(v, current_idx));
- const size_t offset = current_idx + 9;
- idx += len + 8; // skip 8 size bytes + content bytes
- check_length(v.size(), len, offset);
- return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
- }
-
- case 0x7f: // UTF-8 string (indefinite length)
- {
- std::string result;
- while (v.at(idx) != 0xff)
- {
- string_t s = from_cbor_internal(v, idx);
- result += s;
- }
- // skip break byte (0xFF)
- idx += 1;
- return result;
- }
-
- // array (0x00..0x17 data items follow)
- case 0x80:
- case 0x81:
- case 0x82:
- case 0x83:
- case 0x84:
- case 0x85:
- case 0x86:
- case 0x87:
- case 0x88:
- case 0x89:
- case 0x8a:
- case 0x8b:
- case 0x8c:
- case 0x8d:
- case 0x8e:
- case 0x8f:
- case 0x90:
- case 0x91:
- case 0x92:
- case 0x93:
- case 0x94:
- case 0x95:
- case 0x96:
- case 0x97:
- {
- basic_json result = value_t::array;
- const auto len = static_cast<size_t>(v[current_idx] - 0x80);
- for (size_t i = 0; i < len; ++i)
- {
- result.push_back(from_cbor_internal(v, idx));
- }
- return result;
- }
-
- case 0x98: // array (one-byte uint8_t for n follows)
- {
- basic_json result = value_t::array;
- const auto len = static_cast<size_t>(get_from_vector<uint8_t>(v, current_idx));
- idx += 1; // skip 1 size byte
- for (size_t i = 0; i < len; ++i)
- {
- result.push_back(from_cbor_internal(v, idx));
- }
- return result;
- }
-
- case 0x99: // array (two-byte uint16_t for n follow)
- {
- basic_json result = value_t::array;
- const auto len = static_cast<size_t>(get_from_vector<uint16_t>(v, current_idx));
- idx += 2; // skip 4 size bytes
- for (size_t i = 0; i < len; ++i)
- {
- result.push_back(from_cbor_internal(v, idx));
- }
- return result;
- }
-
- case 0x9a: // array (four-byte uint32_t for n follow)
- {
- basic_json result = value_t::array;
- const auto len = static_cast<size_t>(get_from_vector<uint32_t>(v, current_idx));
- idx += 4; // skip 4 size bytes
- for (size_t i = 0; i < len; ++i)
- {
- result.push_back(from_cbor_internal(v, idx));
- }
- return result;
- }
-
- case 0x9b: // array (eight-byte uint64_t for n follow)
- {
- basic_json result = value_t::array;
- const auto len = static_cast<size_t>(get_from_vector<uint64_t>(v, current_idx));
- idx += 8; // skip 8 size bytes
- for (size_t i = 0; i < len; ++i)
- {
- result.push_back(from_cbor_internal(v, idx));
- }
- return result;
- }
-
- case 0x9f: // array (indefinite length)
- {
- basic_json result = value_t::array;
- while (v.at(idx) != 0xff)
- {
- result.push_back(from_cbor_internal(v, idx));
- }
- // skip break byte (0xFF)
- idx += 1;
- return result;
- }
-
- // map (0x00..0x17 pairs of data items follow)
- case 0xa0:
- case 0xa1:
- case 0xa2:
- case 0xa3:
- case 0xa4:
- case 0xa5:
- case 0xa6:
- case 0xa7:
- case 0xa8:
- case 0xa9:
- case 0xaa:
- case 0xab:
- case 0xac:
- case 0xad:
- case 0xae:
- case 0xaf:
- case 0xb0:
- case 0xb1:
- case 0xb2:
- case 0xb3:
- case 0xb4:
- case 0xb5:
- case 0xb6:
- case 0xb7:
- {
- basic_json result = value_t::object;
- const auto len = static_cast<size_t>(v[current_idx] - 0xa0);
- for (size_t i = 0; i < len; ++i)
- {
- std::string key = from_cbor_internal(v, idx);
- result[key] = from_cbor_internal(v, idx);
- }
- return result;
- }
-
- case 0xb8: // map (one-byte uint8_t for n follows)
- {
- basic_json result = value_t::object;
- const auto len = static_cast<size_t>(get_from_vector<uint8_t>(v, current_idx));
- idx += 1; // skip 1 size byte
- for (size_t i = 0; i < len; ++i)
- {
- std::string key = from_cbor_internal(v, idx);
- result[key] = from_cbor_internal(v, idx);
- }
- return result;
- }
-
- case 0xb9: // map (two-byte uint16_t for n follow)
- {
- basic_json result = value_t::object;
- const auto len = static_cast<size_t>(get_from_vector<uint16_t>(v, current_idx));
- idx += 2; // skip 2 size bytes
- for (size_t i = 0; i < len; ++i)
- {
- std::string key = from_cbor_internal(v, idx);
- result[key] = from_cbor_internal(v, idx);
- }
- return result;
- }
-
- case 0xba: // map (four-byte uint32_t for n follow)
- {
- basic_json result = value_t::object;
- const auto len = static_cast<size_t>(get_from_vector<uint32_t>(v, current_idx));
- idx += 4; // skip 4 size bytes
- for (size_t i = 0; i < len; ++i)
- {
- std::string key = from_cbor_internal(v, idx);
- result[key] = from_cbor_internal(v, idx);
- }
- return result;
- }
-
- case 0xbb: // map (eight-byte uint64_t for n follow)
- {
- basic_json result = value_t::object;
- const auto len = static_cast<size_t>(get_from_vector<uint64_t>(v, current_idx));
- idx += 8; // skip 8 size bytes
- for (size_t i = 0; i < len; ++i)
- {
- std::string key = from_cbor_internal(v, idx);
- result[key] = from_cbor_internal(v, idx);
- }
- return result;
- }
-
- case 0xbf: // map (indefinite length)
- {
- basic_json result = value_t::object;
- while (v.at(idx) != 0xff)
- {
- std::string key = from_cbor_internal(v, idx);
- result[key] = from_cbor_internal(v, idx);
- }
- // skip break byte (0xFF)
- idx += 1;
- return result;
- }
-
- case 0xf4: // false
- {
- return false;
- }
-
- case 0xf5: // true
- {
- return true;
- }
-
- case 0xf6: // null
- {
- return value_t::null;
- }
-
- case 0xf9: // Half-Precision Float (two-byte IEEE 754)
- {
- check_length(v.size(), 2, 1);
- idx += 2; // skip two content bytes
-
- // code from RFC 7049, Appendix D, Figure 3:
- // As half-precision floating-point numbers were only added to
- // IEEE 754 in 2008, today's programming platforms often still
- // only have limited support for them. It is very easy to
- // include at least decoding support for them even without such
- // support. An example of a small decoder for half-precision
- // floating-point numbers in the C language is shown in Fig. 3.
- const int half = (v[current_idx + 1] << 8) + v[current_idx + 2];
- const int exp = (half >> 10) & 0x1f;
- const int mant = half & 0x3ff;
- double val;
- if (exp == 0)
- {
- val = std::ldexp(mant, -24);
- }
- else if (exp != 31)
- {
- val = std::ldexp(mant + 1024, exp - 25);
- }
- else
- {
- val = mant == 0 ? INFINITY : NAN;
- }
- return half & 0x8000 ? -val : val;
- }
-
- case 0xfa: // Single-Precision Float (four-byte IEEE 754)
- {
- // copy bytes in reverse order into the float variable
- check_length(v.size(), sizeof(float), 1);
- float res;
- for (size_t byte = 0; byte < sizeof(float); ++byte)
- {
- reinterpret_cast<uint8_t*>(&res)[sizeof(float) - byte - 1] = v[current_idx + 1 + byte];
- }
- idx += sizeof(float); // skip content bytes
- return res;
- }
-
- case 0xfb: // Double-Precision Float (eight-byte IEEE 754)
- {
- check_length(v.size(), sizeof(double), 1);
- // copy bytes in reverse order into the double variable
- double res;
- for (size_t byte = 0; byte < sizeof(double); ++byte)
- {
- reinterpret_cast<uint8_t*>(&res)[sizeof(double) - byte - 1] = v[current_idx + 1 + byte];
- }
- idx += sizeof(double); // skip content bytes
- return res;
- }
-
- default: // anything else (0xFF is handled inside the other types)
- {
- throw std::invalid_argument("error parsing a CBOR @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast<int>(v[current_idx])));
- }
- }
+ binary_writer<char>(o).write_cbor(j);
}
- public:
/*!
@brief create a MessagePack serialization of a given JSON value
@@ -7621,6 +13361,62 @@ class basic_json
serialization format. MessagePack is a binary serialization format which
aims to be more compact than JSON itself, yet more efficient to parse.
+ The library uses the following mapping from JSON values types to
+ MessagePack types according to the MessagePack specification:
+
+ JSON value type | value/range | MessagePack type | first byte
+ --------------- | --------------------------------- | ---------------- | ----------
+ null | `null` | nil | 0xC0
+ boolean | `true` | true | 0xC3
+ boolean | `false` | false | 0xC2
+ number_integer | -9223372036854775808..-2147483649 | int64 | 0xD3
+ number_integer | -2147483648..-32769 | int32 | 0xD2
+ number_integer | -32768..-129 | int16 | 0xD1
+ number_integer | -128..-33 | int8 | 0xD0
+ number_integer | -32..-1 | negative fixint | 0xE0..0xFF
+ number_integer | 0..127 | positive fixint | 0x00..0x7F
+ number_integer | 128..255 | uint 8 | 0xCC
+ number_integer | 256..65535 | uint 16 | 0xCD
+ number_integer | 65536..4294967295 | uint 32 | 0xCE
+ number_integer | 4294967296..18446744073709551615 | uint 64 | 0xCF
+ number_unsigned | 0..127 | positive fixint | 0x00..0x7F
+ number_unsigned | 128..255 | uint 8 | 0xCC
+ number_unsigned | 256..65535 | uint 16 | 0xCD
+ number_unsigned | 65536..4294967295 | uint 32 | 0xCE
+ number_unsigned | 4294967296..18446744073709551615 | uint 64 | 0xCF
+ number_float | *any value* | float 64 | 0xCB
+ string | *length*: 0..31 | fixstr | 0xA0..0xBF
+ string | *length*: 32..255 | str 8 | 0xD9
+ string | *length*: 256..65535 | str 16 | 0xDA
+ string | *length*: 65536..4294967295 | str 32 | 0xDB
+ array | *size*: 0..15 | fixarray | 0x90..0x9F
+ array | *size*: 16..65535 | array 16 | 0xDC
+ array | *size*: 65536..4294967295 | array 32 | 0xDD
+ object | *size*: 0..15 | fix map | 0x80..0x8F
+ object | *size*: 16..65535 | map 16 | 0xDE
+ object | *size*: 65536..4294967295 | map 32 | 0xDF
+
+ @note The mapping is **complete** in the sense that any JSON value type
+ can be converted to a MessagePack value.
+
+ @note The following values can **not** be converted to a MessagePack value:
+ - strings with more than 4294967295 bytes
+ - arrays with more than 4294967295 elements
+ - objects with more than 4294967295 elements
+
+ @note The following MessagePack types are not used in the conversion:
+ - bin 8 - bin 32 (0xC4..0xC6)
+ - ext 8 - ext 32 (0xC7..0xC9)
+ - float 32 (0xCA)
+ - fixext 1 - fixext 16 (0xD4..0xD8)
+
+ @note Any MessagePack output created @ref to_msgpack can be successfully
+ parsed by @ref from_msgpack.
+
+ @note If NaN or Infinity are stored inside a JSON number, they are
+ serialized properly. This behavior differs from the @ref dump()
+ function which serializes NaN or Infinity to `null`.
+
@param[in] j JSON value to serialize
@return MessagePack serialization as byte vector
@@ -7630,3893 +13426,224 @@ class basic_json
vector in MessagePack format.,to_msgpack}
@sa http://msgpack.org
- @sa @ref from_msgpack(const std::vector<uint8_t>&) for the analogous
- deserialization
+ @sa @ref from_msgpack(const std::vector<uint8_t>&, const size_t) for the
+ analogous deserialization
@sa @ref to_cbor(const basic_json& for the related CBOR format
- */
- static std::vector<uint8_t> to_msgpack(const basic_json& j)
- {
- std::vector<uint8_t> result;
- to_msgpack_internal(j, result);
- return result;
- }
-
- /*!
- @brief create a JSON value from a byte vector in MessagePack format
-
- Deserializes a given byte vector @a v to a JSON value using the MessagePack
- serialization format.
- @param[in] v a byte vector in MessagePack format
- @return deserialized JSON value
-
- @throw std::invalid_argument if unsupported features from MessagePack were
- used in the given vector @a v or if the input is not valid MessagePack
- @throw std::out_of_range if the given vector ends prematurely
-
- @complexity Linear in the size of the byte vector @a v.
-
- @liveexample{The example shows the deserialization of a byte vector in
- MessagePack format to a JSON value.,from_msgpack}
-
- @sa http://msgpack.org
- @sa @ref to_msgpack(const basic_json&) for the analogous serialization
- @sa @ref from_cbor(const std::vector<uint8_t>&) for the related CBOR format
- */
- static basic_json from_msgpack(const std::vector<uint8_t>& v)
- {
- size_t i = 0;
- return from_msgpack_internal(v, i);
- }
-
- /*!
- @brief create a MessagePack serialization of a given JSON value
-
- Serializes a given JSON value @a j to a byte vector using the CBOR (Concise
- Binary Object Representation) serialization format. CBOR is a binary
- serialization format which aims to be more compact than JSON itself, yet
- more efficient to parse.
-
- @param[in] j JSON value to serialize
- @return MessagePack serialization as byte vector
-
- @complexity Linear in the size of the JSON value @a j.
-
- @liveexample{The example shows the serialization of a JSON value to a byte
- vector in CBOR format.,to_cbor}
-
- @sa http://cbor.io
- @sa @ref from_cbor(const std::vector<uint8_t>&) for the analogous
- deserialization
- @sa @ref to_msgpack(const basic_json& for the related MessagePack format
+ @since version 2.0.9
*/
- static std::vector<uint8_t> to_cbor(const basic_json& j)
+ static std::vector<uint8_t> to_msgpack(const basic_json& j)
{
std::vector<uint8_t> result;
- to_cbor_internal(j, result);
+ to_msgpack(j, result);
return result;
}
- /*!
- @brief create a JSON value from a byte vector in CBOR format
-
- Deserializes a given byte vector @a v to a JSON value using the CBOR
- (Concise Binary Object Representation) serialization format.
-
- @param[in] v a byte vector in CBOR format
+ static void to_msgpack(const basic_json& j, detail::output_adapter<uint8_t> o)
+ {
+ binary_writer<uint8_t>(o).write_msgpack(j);
+ }
+
+ static void to_msgpack(const basic_json& j, detail::output_adapter<char> o)
+ {
+ binary_writer<char>(o).write_msgpack(j);
+ }
+
+ /*!
+ @brief create a JSON value from an input in CBOR format
+
+ Deserializes a given input @a i to a JSON value using the CBOR (Concise
+ Binary Object Representation) serialization format.
+
+ The library maps CBOR types to JSON value types as follows:
+
+ CBOR type | JSON value type | first byte
+ ---------------------- | --------------- | ----------
+ Integer | number_unsigned | 0x00..0x17
+ Unsigned integer | number_unsigned | 0x18
+ Unsigned integer | number_unsigned | 0x19
+ Unsigned integer | number_unsigned | 0x1A
+ Unsigned integer | number_unsigned | 0x1B
+ Negative integer | number_integer | 0x20..0x37
+ Negative integer | number_integer | 0x38
+ Negative integer | number_integer | 0x39
+ Negative integer | number_integer | 0x3A
+ Negative integer | number_integer | 0x3B
+ Negative integer | number_integer | 0x40..0x57
+ UTF-8 string | string | 0x60..0x77
+ UTF-8 string | string | 0x78
+ UTF-8 string | string | 0x79
+ UTF-8 string | string | 0x7A
+ UTF-8 string | string | 0x7B
+ UTF-8 string | string | 0x7F
+ array | array | 0x80..0x97
+ array | array | 0x98
+ array | array | 0x99
+ array | array | 0x9A
+ array | array | 0x9B
+ array | array | 0x9F
+ map | object | 0xA0..0xB7
+ map | object | 0xB8
+ map | object | 0xB9
+ map | object | 0xBA
+ map | object | 0xBB
+ map | object | 0xBF
+ False | `false` | 0xF4
+ True | `true` | 0xF5
+ Nill | `null` | 0xF6
+ Half-Precision Float | number_float | 0xF9
+ Single-Precision Float | number_float | 0xFA
+ Double-Precision Float | number_float | 0xFB
+
+ @warning The mapping is **incomplete** in the sense that not all CBOR
+ types can be converted to a JSON value. The following CBOR types
+ are not supported and will yield parse errors (parse_error.112):
+ - byte strings (0x40..0x5F)
+ - date/time (0xC0..0xC1)
+ - bignum (0xC2..0xC3)
+ - decimal fraction (0xC4)
+ - bigfloat (0xC5)
+ - tagged items (0xC6..0xD4, 0xD8..0xDB)
+ - expected conversions (0xD5..0xD7)
+ - simple values (0xE0..0xF3, 0xF8)
+ - undefined (0xF7)
+
+ @warning CBOR allows map keys of any type, whereas JSON only allows
+ strings as keys in object values. Therefore, CBOR maps with keys
+ other than UTF-8 strings are rejected (parse_error.113).
+
+ @note Any CBOR output created @ref to_cbor can be successfully parsed by
+ @ref from_cbor.
+
+ @param[in] i an input in CBOR format convertible to an input adapter
+ @param[in] strict whether to expect the input to be consumed until EOF
+ (true by default)
@return deserialized JSON value
- @throw std::invalid_argument if unsupported features from CBOR were used in
- the given vector @a v or if the input is not valid MessagePack
- @throw std::out_of_range if the given vector ends prematurely
+ @throw parse_error.110 if the given input ends prematurely or the end of
+ file was not reached when @a strict was set to true
+ @throw parse_error.112 if unsupported features from CBOR were
+ used in the given input @a v or if the input is not valid CBOR
+ @throw parse_error.113 if a string was expected as map key, but not found
- @complexity Linear in the size of the byte vector @a v.
+ @complexity Linear in the size of the input @a i.
@liveexample{The example shows the deserialization of a byte vector in CBOR
format to a JSON value.,from_cbor}
@sa http://cbor.io
@sa @ref to_cbor(const basic_json&) for the analogous serialization
- @sa @ref from_msgpack(const std::vector<uint8_t>&) for the related
- MessagePack format
- */
- static basic_json from_cbor(const std::vector<uint8_t>& v)
- {
- size_t i = 0;
- return from_cbor_internal(v, i);
- }
-
- /// @}
-
- private:
- ///////////////////////////
- // convenience functions //
- ///////////////////////////
-
- /*!
- @brief return the type as string
-
- Returns the type name as string to be used in error messages - usually to
- indicate that a function was called on a wrong JSON type.
+ @sa @ref from_msgpack(detail::input_adapter, const bool) for the
+ related MessagePack format
- @return basically a string representation of a the @a m_type member
-
- @complexity Constant.
-
- @since version 1.0.0
+ @since version 2.0.9; parameter @a start_index since 2.1.1; changed to
+ consume input adapters, removed start_index parameter, and added
+ @a strict parameter since 3.0.0
*/
- std::string type_name() const
+ static basic_json from_cbor(detail::input_adapter i,
+ const bool strict = true)
{
- switch (m_type)
- {
- case value_t::null:
- return "null";
- case value_t::object:
- return "object";
- case value_t::array:
- return "array";
- case value_t::string:
- return "string";
- case value_t::boolean:
- return "boolean";
- case value_t::discarded:
- return "discarded";
- default:
- return "number";
- }
+ return binary_reader(i).parse_cbor(strict);
}
/*!
- @brief calculates the extra space to escape a JSON string
-
- @param[in] s the string to escape
- @return the number of characters required to escape string @a s
-
- @complexity Linear in the length of string @a s.
+ @copydoc from_cbor(detail::input_adapter, const bool)
*/
- static std::size_t extra_space(const string_t& s) noexcept
+ template<typename A1, typename A2,
+ detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>
+ static basic_json from_cbor(A1 && a1, A2 && a2, const bool strict = true)
{
- return std::accumulate(s.begin(), s.end(), size_t{},
- [](size_t res, typename string_t::value_type c)
- {
- switch (c)
- {
- case '"':
- case '\\':
- case '\b':
- case '\f':
- case '\n':
- case '\r':
- case '\t':
- {
- // from c (1 byte) to \x (2 bytes)
- return res + 1;
- }
-
- default:
- {
- if (c >= 0x00 and c <= 0x1f)
- {
- // from c (1 byte) to \uxxxx (6 bytes)
- return res + 5;
- }
- else
- {
- return res;
- }
- }
- }
- });
+ return binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).parse_cbor(strict);
}
/*!
- @brief escape a string
-
- Escape a string by replacing certain special characters by a sequence of
- an escape character (backslash) and another character and other control
- characters by a sequence of "\u" followed by a four-digit hex
- representation.
-
- @param[in] s the string to escape
- @return the escaped string
+ @brief create a JSON value from an input in MessagePack format
- @complexity Linear in the length of string @a s.
- */
- static string_t escape_string(const string_t& s)
- {
- const auto space = extra_space(s);
- if (space == 0)
- {
- return s;
- }
-
- // create a result string of necessary size
- string_t result(s.size() + space, '\\');
- std::size_t pos = 0;
-
- for (const auto& c : s)
- {
- switch (c)
- {
- // quotation mark (0x22)
- case '"':
- {
- result[pos + 1] = '"';
- pos += 2;
- break;
- }
-
- // reverse solidus (0x5c)
- case '\\':
- {
- // nothing to change
- pos += 2;
- break;
- }
-
- // backspace (0x08)
- case '\b':
- {
- result[pos + 1] = 'b';
- pos += 2;
- break;
- }
-
- // formfeed (0x0c)
- case '\f':
- {
- result[pos + 1] = 'f';
- pos += 2;
- break;
- }
-
- // newline (0x0a)
- case '\n':
- {
- result[pos + 1] = 'n';
- pos += 2;
- break;
- }
-
- // carriage return (0x0d)
- case '\r':
- {
- result[pos + 1] = 'r';
- pos += 2;
- break;
- }
-
- // horizontal tab (0x09)
- case '\t':
- {
- result[pos + 1] = 't';
- pos += 2;
- break;
- }
-
- default:
- {
- if (c >= 0x00 and c <= 0x1f)
- {
- // convert a number 0..15 to its hex representation
- // (0..f)
- static const char hexify[16] =
- {
- '0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
- };
-
- // print character c as \uxxxx
- for (const char m :
- { 'u', '0', '0', hexify[c >> 4], hexify[c & 0x0f]
- })
- {
- result[++pos] = m;
- }
-
- ++pos;
- }
- else
- {
- // all other characters are added as-is
- result[pos++] = c;
- }
- break;
- }
- }
- }
-
- return result;
- }
+ Deserializes a given input @a i to a JSON value using the MessagePack
+ serialization format.
- /*!
- @brief internal implementation of the serialization function
+ The library maps MessagePack types to JSON value types as follows:
+
+ MessagePack type | JSON value type | first byte
+ ---------------- | --------------- | ----------
+ positive fixint | number_unsigned | 0x00..0x7F
+ fixmap | object | 0x80..0x8F
+ fixarray | array | 0x90..0x9F
+ fixstr | string | 0xA0..0xBF
+ nil | `null` | 0xC0
+ false | `false` | 0xC2
+ true | `true` | 0xC3
+ float 32 | number_float | 0xCA
+ float 64 | number_float | 0xCB
+ uint 8 | number_unsigned | 0xCC
+ uint 16 | number_unsigned | 0xCD
+ uint 32 | number_unsigned | 0xCE
+ uint 64 | number_unsigned | 0xCF
+ int 8 | number_integer | 0xD0
+ int 16 | number_integer | 0xD1
+ int 32 | number_integer | 0xD2
+ int 64 | number_integer | 0xD3
+ str 8 | string | 0xD9
+ str 16 | string | 0xDA
+ str 32 | string | 0xDB
+ array 16 | array | 0xDC
+ array 32 | array | 0xDD
+ map 16 | object | 0xDE
+ map 32 | object | 0xDF
+ negative fixint | number_integer | 0xE0-0xFF
+
+ @warning The mapping is **incomplete** in the sense that not all
+ MessagePack types can be converted to a JSON value. The following
+ MessagePack types are not supported and will yield parse errors:
+ - bin 8 - bin 32 (0xC4..0xC6)
+ - ext 8 - ext 32 (0xC7..0xC9)
+ - fixext 1 - fixext 16 (0xD4..0xD8)
+
+ @note Any MessagePack output created @ref to_msgpack can be successfully
+ parsed by @ref from_msgpack.
+
+ @param[in] i an input in MessagePack format convertible to an input
+ adapter
+ @param[in] strict whether to expect the input to be consumed until EOF
+ (true by default)
+
+ @throw parse_error.110 if the given input ends prematurely or the end of
+ file was not reached when @a strict was set to true
+ @throw parse_error.112 if unsupported features from MessagePack were
+ used in the given input @a i or if the input is not valid MessagePack
+ @throw parse_error.113 if a string was expected as map key, but not found
+
+ @complexity Linear in the size of the input @a i.
- This function is called by the public member function dump and organizes
- the serialization internally. The indentation level is propagated as
- additional parameter. In case of arrays and objects, the function is
- called recursively. Note that
+ @liveexample{The example shows the deserialization of a byte vector in
+ MessagePack format to a JSON value.,from_msgpack}
- - strings and object keys are escaped using `escape_string()`
- - integer numbers are converted implicitly via `operator<<`
- - floating-point numbers are converted to a string using `"%g"` format
+ @sa http://msgpack.org
+ @sa @ref to_msgpack(const basic_json&) for the analogous serialization
+ @sa @ref from_cbor(detail::input_adapter, const bool) for the related CBOR
+ format
- @param[out] o stream to write to
- @param[in] pretty_print whether the output shall be pretty-printed
- @param[in] indent_step the indent level
- @param[in] current_indent the current indent level (only used internally)
+ @since version 2.0.9; parameter @a start_index since 2.1.1; changed to
+ consume input adapters, removed start_index parameter, and added
+ @a strict parameter since 3.0.0
*/
- void dump(std::ostream& o,
- const bool pretty_print,
- const unsigned int indent_step,
- const unsigned int current_indent = 0) const
+ static basic_json from_msgpack(detail::input_adapter i,
+ const bool strict = true)
{
- // variable to hold indentation for recursive calls
- unsigned int new_indent = current_indent;
-
- switch (m_type)
- {
- case value_t::object:
- {
- if (m_value.object->empty())
- {
- o << "{}";
- return;
- }
-
- o << "{";
-
- // increase indentation
- if (pretty_print)
- {
- new_indent += indent_step;
- o << "\n";
- }
-
- for (auto i = m_value.object->cbegin(); i != m_value.object->cend(); ++i)
- {
- if (i != m_value.object->cbegin())
- {
- o << (pretty_print ? ",\n" : ",");
- }
- o << string_t(new_indent, ' ') << "\""
- << escape_string(i->first) << "\":"
- << (pretty_print ? " " : "");
- i->second.dump(o, pretty_print, indent_step, new_indent);
- }
-
- // decrease indentation
- if (pretty_print)
- {
- new_indent -= indent_step;
- o << "\n";
- }
-
- o << string_t(new_indent, ' ') + "}";
- return;
- }
-
- case value_t::array:
- {
- if (m_value.array->empty())
- {
- o << "[]";
- return;
- }
-
- o << "[";
-
- // increase indentation
- if (pretty_print)
- {
- new_indent += indent_step;
- o << "\n";
- }
-
- for (auto i = m_value.array->cbegin(); i != m_value.array->cend(); ++i)
- {
- if (i != m_value.array->cbegin())
- {
- o << (pretty_print ? ",\n" : ",");
- }
- o << string_t(new_indent, ' ');
- i->dump(o, pretty_print, indent_step, new_indent);
- }
-
- // decrease indentation
- if (pretty_print)
- {
- new_indent -= indent_step;
- o << "\n";
- }
-
- o << string_t(new_indent, ' ') << "]";
- return;
- }
-
- case value_t::string:
- {
- o << string_t("\"") << escape_string(*m_value.string) << "\"";
- return;
- }
-
- case value_t::boolean:
- {
- o << (m_value.boolean ? "true" : "false");
- return;
- }
-
- case value_t::number_integer:
- {
- o << m_value.number_integer;
- return;
- }
-
- case value_t::number_unsigned:
- {
- o << m_value.number_unsigned;
- return;
- }
-
- case value_t::number_float:
- {
- if (m_value.number_float == 0)
- {
- // special case for zero to get "0.0"/"-0.0"
- o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0");
- }
- else
- {
- o << m_value.number_float;
- }
- return;
- }
-
- case value_t::discarded:
- {
- o << "<discarded>";
- return;
- }
-
- case value_t::null:
- {
- o << "null";
- return;
- }
- }
+ return binary_reader(i).parse_msgpack(strict);
}
- private:
- //////////////////////
- // member variables //
- //////////////////////
-
- /// the type of the current element
- value_t m_type = value_t::null;
-
- /// the value of the current element
- json_value m_value = {};
-
-
- private:
- ///////////////
- // iterators //
- ///////////////
-
- /*!
- @brief an iterator for primitive JSON types
-
- This class models an iterator for primitive JSON types (boolean, number,
- string). It's only purpose is to allow the iterator/const_iterator classes
- to "iterate" over primitive values. Internally, the iterator is modeled by
- a `difference_type` variable. Value begin_value (`0`) models the begin,
- end_value (`1`) models past the end.
- */
- class primitive_iterator_t
- {
- public:
- /// set iterator to a defined beginning
- void set_begin() noexcept
- {
- m_it = begin_value;
- }
-
- /// set iterator to a defined past the end
- void set_end() noexcept
- {
- m_it = end_value;
- }
-
- /// return whether the iterator can be dereferenced
- constexpr bool is_begin() const noexcept
- {
- return (m_it == begin_value);
- }
-
- /// return whether the iterator is at end
- constexpr bool is_end() const noexcept
- {
- return (m_it == end_value);
- }
-
- /// return reference to the value to change and compare
- operator difference_type& () noexcept
- {
- return m_it;
- }
-
- /// return value to compare
- constexpr operator difference_type () const noexcept
- {
- return m_it;
- }
-
- private:
- static constexpr difference_type begin_value = 0;
- static constexpr difference_type end_value = begin_value + 1;
-
- /// iterator as signed integer type
- difference_type m_it = std::numeric_limits<std::ptrdiff_t>::denorm_min();
- };
-
- /*!
- @brief an iterator value
-
- @note This structure could easily be a union, but MSVC currently does not
- allow unions members with complex constructors, see
- https://github.com/nlohmann/json/pull/105.
- */
- struct internal_iterator
- {
- /// iterator for JSON objects
- typename object_t::iterator object_iterator;
- /// iterator for JSON arrays
- typename array_t::iterator array_iterator;
- /// generic iterator for all other types
- primitive_iterator_t primitive_iterator;
-
- /// create an uninitialized internal_iterator
- internal_iterator() noexcept
- : object_iterator(), array_iterator(), primitive_iterator()
- {}
- };
-
- /// proxy class for the iterator_wrapper functions
- template<typename IteratorType>
- class iteration_proxy
- {
- private:
- /// helper class for iteration
- class iteration_proxy_internal
- {
- private:
- /// the iterator
- IteratorType anchor;
- /// an index for arrays (used to create key names)
- size_t array_index = 0;
-
- public:
- explicit iteration_proxy_internal(IteratorType it) noexcept
- : anchor(it)
- {}
-
- /// dereference operator (needed for range-based for)
- iteration_proxy_internal& operator*()
- {
- return *this;
- }
-
- /// increment operator (needed for range-based for)
- iteration_proxy_internal& operator++()
- {
- ++anchor;
- ++array_index;
-
- return *this;
- }
-
- /// inequality operator (needed for range-based for)
- bool operator!= (const iteration_proxy_internal& o) const
- {
- return anchor != o.anchor;
- }
-
- /// return key of the iterator
- typename basic_json::string_t key() const
- {
- assert(anchor.m_object != nullptr);
-
- switch (anchor.m_object->type())
- {
- // use integer array index as key
- case value_t::array:
- {
- return std::to_string(array_index);
- }
-
- // use key from the object
- case value_t::object:
- {
- return anchor.key();
- }
-
- // use an empty key for all primitive types
- default:
- {
- return "";
- }
- }
- }
-
- /// return value of the iterator
- typename IteratorType::reference value() const
- {
- return anchor.value();
- }
- };
-
- /// the container to iterate
- typename IteratorType::reference container;
-
- public:
- /// construct iteration proxy from a container
- explicit iteration_proxy(typename IteratorType::reference cont)
- : container(cont)
- {}
-
- /// return iterator begin (needed for range-based for)
- iteration_proxy_internal begin() noexcept
- {
- return iteration_proxy_internal(container.begin());
- }
-
- /// return iterator end (needed for range-based for)
- iteration_proxy_internal end() noexcept
- {
- return iteration_proxy_internal(container.end());
- }
- };
-
- public:
- /*!
- @brief a template for a random access iterator for the @ref basic_json class
-
- This class implements a both iterators (iterator and const_iterator) for the
- @ref basic_json class.
-
- @note An iterator is called *initialized* when a pointer to a JSON value
- has been set (e.g., by a constructor or a copy assignment). If the
- iterator is default-constructed, it is *uninitialized* and most
- methods are undefined. **The library uses assertions to detect calls
- on uninitialized iterators.**
-
- @requirement The class satisfies the following concept requirements:
- - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator):
- The iterator that can be moved to point (forward and backward) to any
- element in constant time.
-
- @since version 1.0.0, simplified in version 2.0.9
- */
- template<typename U>
- class iter_impl : public std::iterator<std::random_access_iterator_tag, U>
- {
- /// allow basic_json to access private members
- friend class basic_json;
-
- // make sure U is basic_json or const basic_json
- static_assert(std::is_same<U, basic_json>::value
- or std::is_same<U, const basic_json>::value,
- "iter_impl only accepts (const) basic_json");
-
- public:
- /// the type of the values when the iterator is dereferenced
- using value_type = typename basic_json::value_type;
- /// a type to represent differences between iterators
- using difference_type = typename basic_json::difference_type;
- /// defines a pointer to the type iterated over (value_type)
- using pointer = typename std::conditional<std::is_const<U>::value,
- typename basic_json::const_pointer,
- typename basic_json::pointer>::type;
- /// defines a reference to the type iterated over (value_type)
- using reference = typename std::conditional<std::is_const<U>::value,
- typename basic_json::const_reference,
- typename basic_json::reference>::type;
- /// the category of the iterator
- using iterator_category = std::bidirectional_iterator_tag;
-
- /// default constructor
- iter_impl() = default;
-
- /*!
- @brief constructor for a given JSON instance
- @param[in] object pointer to a JSON object for this iterator
- @pre object != nullptr
- @post The iterator is initialized; i.e. `m_object != nullptr`.
- */
- explicit iter_impl(pointer object) noexcept
- : m_object(object)
- {
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- m_it.object_iterator = typename object_t::iterator();
- break;
- }
-
- case basic_json::value_t::array:
- {
- m_it.array_iterator = typename array_t::iterator();
- break;
- }
-
- default:
- {
- m_it.primitive_iterator = primitive_iterator_t();
- break;
- }
- }
- }
-
- /*
- Use operator `const_iterator` instead of `const_iterator(const iterator&
- other) noexcept` to avoid two class definitions for @ref iterator and
- @ref const_iterator.
-
- This function is only called if this class is an @ref iterator. If this
- class is a @ref const_iterator this function is not called.
- */
- operator const_iterator() const
- {
- const_iterator ret;
-
- if (m_object)
- {
- ret.m_object = m_object;
- ret.m_it = m_it;
- }
-
- return ret;
- }
-
- /*!
- @brief copy constructor
- @param[in] other iterator to copy from
- @note It is not checked whether @a other is initialized.
- */
- iter_impl(const iter_impl& other) noexcept
- : m_object(other.m_object), m_it(other.m_it)
- {}
-
- /*!
- @brief copy assignment
- @param[in,out] other iterator to copy from
- @note It is not checked whether @a other is initialized.
- */
- iter_impl& operator=(iter_impl other) noexcept(
- std::is_nothrow_move_constructible<pointer>::value and
- std::is_nothrow_move_assignable<pointer>::value and
- std::is_nothrow_move_constructible<internal_iterator>::value and
- std::is_nothrow_move_assignable<internal_iterator>::value
- )
- {
- std::swap(m_object, other.m_object);
- std::swap(m_it, other.m_it);
- return *this;
- }
-
- private:
- /*!
- @brief set the iterator to the first value
- @pre The iterator is initialized; i.e. `m_object != nullptr`.
- */
- void set_begin() noexcept
- {
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- m_it.object_iterator = m_object->m_value.object->begin();
- break;
- }
-
- case basic_json::value_t::array:
- {
- m_it.array_iterator = m_object->m_value.array->begin();
- break;
- }
-
- case basic_json::value_t::null:
- {
- // set to end so begin()==end() is true: null is empty
- m_it.primitive_iterator.set_end();
- break;
- }
-
- default:
- {
- m_it.primitive_iterator.set_begin();
- break;
- }
- }
- }
-
- /*!
- @brief set the iterator past the last value
- @pre The iterator is initialized; i.e. `m_object != nullptr`.
- */
- void set_end() noexcept
- {
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- m_it.object_iterator = m_object->m_value.object->end();
- break;
- }
-
- case basic_json::value_t::array:
- {
- m_it.array_iterator = m_object->m_value.array->end();
- break;
- }
-
- default:
- {
- m_it.primitive_iterator.set_end();
- break;
- }
- }
- }
-
- public:
- /*!
- @brief return a reference to the value pointed to by the iterator
- @pre The iterator is initialized; i.e. `m_object != nullptr`.
- */
- reference operator*() const
- {
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- assert(m_it.object_iterator != m_object->m_value.object->end());
- return m_it.object_iterator->second;
- }
-
- case basic_json::value_t::array:
- {
- assert(m_it.array_iterator != m_object->m_value.array->end());
- return *m_it.array_iterator;
- }
-
- case basic_json::value_t::null:
- {
- throw std::out_of_range("cannot get value");
- }
-
- default:
- {
- if (m_it.primitive_iterator.is_begin())
- {
- return *m_object;
- }
- else
- {
- throw std::out_of_range("cannot get value");
- }
- }
- }
- }
-
- /*!
- @brief dereference the iterator
- @pre The iterator is initialized; i.e. `m_object != nullptr`.
- */
- pointer operator->() const
- {
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- assert(m_it.object_iterator != m_object->m_value.object->end());
- return &(m_it.object_iterator->second);
- }
-
- case basic_json::value_t::array:
- {
- assert(m_it.array_iterator != m_object->m_value.array->end());
- return &*m_it.array_iterator;
- }
-
- default:
- {
- if (m_it.primitive_iterator.is_begin())
- {
- return m_object;
- }
- else
- {
- throw std::out_of_range("cannot get value");
- }
- }
- }
- }
-
- /*!
- @brief post-increment (it++)
- @pre The iterator is initialized; i.e. `m_object != nullptr`.
- */
- iter_impl operator++(int)
- {
- auto result = *this;
- ++(*this);
- return result;
- }
-
- /*!
- @brief pre-increment (++it)
- @pre The iterator is initialized; i.e. `m_object != nullptr`.
- */
- iter_impl& operator++()
- {
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- std::advance(m_it.object_iterator, 1);
- break;
- }
-
- case basic_json::value_t::array:
- {
- std::advance(m_it.array_iterator, 1);
- break;
- }
-
- default:
- {
- ++m_it.primitive_iterator;
- break;
- }
- }
-
- return *this;
- }
-
- /*!
- @brief post-decrement (it--)
- @pre The iterator is initialized; i.e. `m_object != nullptr`.
- */
- iter_impl operator--(int)
- {
- auto result = *this;
- --(*this);
- return result;
- }
-
- /*!
- @brief pre-decrement (--it)
- @pre The iterator is initialized; i.e. `m_object != nullptr`.
- */
- iter_impl& operator--()
- {
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- std::advance(m_it.object_iterator, -1);
- break;
- }
-
- case basic_json::value_t::array:
- {
- std::advance(m_it.array_iterator, -1);
- break;
- }
-
- default:
- {
- --m_it.primitive_iterator;
- break;
- }
- }
-
- return *this;
- }
-
- /*!
- @brief comparison: equal
- @pre The iterator is initialized; i.e. `m_object != nullptr`.
- */
- bool operator==(const iter_impl& other) const
- {
- // if objects are not the same, the comparison is undefined
- if (m_object != other.m_object)
- {
- throw std::domain_error("cannot compare iterators of different containers");
- }
-
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- return (m_it.object_iterator == other.m_it.object_iterator);
- }
-
- case basic_json::value_t::array:
- {
- return (m_it.array_iterator == other.m_it.array_iterator);
- }
-
- default:
- {
- return (m_it.primitive_iterator == other.m_it.primitive_iterator);
- }
- }
- }
-
- /*!
- @brief comparison: not equal
- @pre The iterator is initialized; i.e. `m_object != nullptr`.
- */
- bool operator!=(const iter_impl& other) const
- {
- return not operator==(other);
- }
-
- /*!
- @brief comparison: smaller
- @pre The iterator is initialized; i.e. `m_object != nullptr`.
- */
- bool operator<(const iter_impl& other) const
- {
- // if objects are not the same, the comparison is undefined
- if (m_object != other.m_object)
- {
- throw std::domain_error("cannot compare iterators of different containers");
- }
-
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- throw std::domain_error("cannot compare order of object iterators");
- }
-
- case basic_json::value_t::array:
- {
- return (m_it.array_iterator < other.m_it.array_iterator);
- }
-
- default:
- {
- return (m_it.primitive_iterator < other.m_it.primitive_iterator);
- }
- }
- }
-
- /*!
- @brief comparison: less than or equal
- @pre The iterator is initialized; i.e. `m_object != nullptr`.
- */
- bool operator<=(const iter_impl& other) const
- {
- return not other.operator < (*this);
- }
-
- /*!
- @brief comparison: greater than
- @pre The iterator is initialized; i.e. `m_object != nullptr`.
- */
- bool operator>(const iter_impl& other) const
- {
- return not operator<=(other);
- }
-
- /*!
- @brief comparison: greater than or equal
- @pre The iterator is initialized; i.e. `m_object != nullptr`.
- */
- bool operator>=(const iter_impl& other) const
- {
- return not operator<(other);
- }
-
- /*!
- @brief add to iterator
- @pre The iterator is initialized; i.e. `m_object != nullptr`.
- */
- iter_impl& operator+=(difference_type i)
- {
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- throw std::domain_error("cannot use offsets with object iterators");
- }
-
- case basic_json::value_t::array:
- {
- std::advance(m_it.array_iterator, i);
- break;
- }
-
- default:
- {
- m_it.primitive_iterator += i;
- break;
- }
- }
-
- return *this;
- }
-
- /*!
- @brief subtract from iterator
- @pre The iterator is initialized; i.e. `m_object != nullptr`.
- */
- iter_impl& operator-=(difference_type i)
- {
- return operator+=(-i);
- }
-
- /*!
- @brief add to iterator
- @pre The iterator is initialized; i.e. `m_object != nullptr`.
- */
- iter_impl operator+(difference_type i)
- {
- auto result = *this;
- result += i;
- return result;
- }
-
- /*!
- @brief subtract from iterator
- @pre The iterator is initialized; i.e. `m_object != nullptr`.
- */
- iter_impl operator-(difference_type i)
- {
- auto result = *this;
- result -= i;
- return result;
- }
-
- /*!
- @brief return difference
- @pre The iterator is initialized; i.e. `m_object != nullptr`.
- */
- difference_type operator-(const iter_impl& other) const
- {
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- throw std::domain_error("cannot use offsets with object iterators");
- }
-
- case basic_json::value_t::array:
- {
- return m_it.array_iterator - other.m_it.array_iterator;
- }
-
- default:
- {
- return m_it.primitive_iterator - other.m_it.primitive_iterator;
- }
- }
- }
-
- /*!
- @brief access to successor
- @pre The iterator is initialized; i.e. `m_object != nullptr`.
- */
- reference operator[](difference_type n) const
- {
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- throw std::domain_error("cannot use operator[] for object iterators");
- }
-
- case basic_json::value_t::array:
- {
- return *std::next(m_it.array_iterator, n);
- }
-
- case basic_json::value_t::null:
- {
- throw std::out_of_range("cannot get value");
- }
-
- default:
- {
- if (m_it.primitive_iterator == -n)
- {
- return *m_object;
- }
- else
- {
- throw std::out_of_range("cannot get value");
- }
- }
- }
- }
-
- /*!
- @brief return the key of an object iterator
- @pre The iterator is initialized; i.e. `m_object != nullptr`.
- */
- typename object_t::key_type key() const
- {
- assert(m_object != nullptr);
-
- if (m_object->is_object())
- {
- return m_it.object_iterator->first;
- }
- else
- {
- throw std::domain_error("cannot use key() for non-object iterators");
- }
- }
-
- /*!
- @brief return the value of an iterator
- @pre The iterator is initialized; i.e. `m_object != nullptr`.
- */
- reference value() const
- {
- return operator*();
- }
-
- private:
- /// associated JSON instance
- pointer m_object = nullptr;
- /// the actual iterator of the associated instance
- internal_iterator m_it = internal_iterator();
- };
-
- /*!
- @brief a template for a reverse iterator class
-
- @tparam Base the base iterator type to reverse. Valid types are @ref
- iterator (to create @ref reverse_iterator) and @ref const_iterator (to
- create @ref const_reverse_iterator).
-
- @requirement The class satisfies the following concept requirements:
- - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator):
- The iterator that can be moved to point (forward and backward) to any
- element in constant time.
- - [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator):
- It is possible to write to the pointed-to element (only if @a Base is
- @ref iterator).
-
- @since version 1.0.0
- */
- template<typename Base>
- class json_reverse_iterator : public std::reverse_iterator<Base>
- {
- public:
- /// shortcut to the reverse iterator adaptor
- using base_iterator = std::reverse_iterator<Base>;
- /// the reference type for the pointed-to element
- using reference = typename Base::reference;
-
- /// create reverse iterator from iterator
- json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept
- : base_iterator(it)
- {}
-
- /// create reverse iterator from base class
- json_reverse_iterator(const base_iterator& it) noexcept
- : base_iterator(it)
- {}
-
- /// post-increment (it++)
- json_reverse_iterator operator++(int)
- {
- return base_iterator::operator++(1);
- }
-
- /// pre-increment (++it)
- json_reverse_iterator& operator++()
- {
- base_iterator::operator++();
- return *this;
- }
-
- /// post-decrement (it--)
- json_reverse_iterator operator--(int)
- {
- return base_iterator::operator--(1);
- }
-
- /// pre-decrement (--it)
- json_reverse_iterator& operator--()
- {
- base_iterator::operator--();
- return *this;
- }
-
- /// add to iterator
- json_reverse_iterator& operator+=(difference_type i)
- {
- base_iterator::operator+=(i);
- return *this;
- }
-
- /// add to iterator
- json_reverse_iterator operator+(difference_type i) const
- {
- auto result = *this;
- result += i;
- return result;
- }
-
- /// subtract from iterator
- json_reverse_iterator operator-(difference_type i) const
- {
- auto result = *this;
- result -= i;
- return result;
- }
-
- /// return difference
- difference_type operator-(const json_reverse_iterator& other) const
- {
- return this->base() - other.base();
- }
-
- /// access to successor
- reference operator[](difference_type n) const
- {
- return *(this->operator+(n));
- }
-
- /// return the key of an object iterator
- typename object_t::key_type key() const
- {
- auto it = --this->base();
- return it.key();
- }
-
- /// return the value of an iterator
- reference value() const
- {
- auto it = --this->base();
- return it.operator * ();
- }
- };
-
-
- private:
- //////////////////////
- // lexer and parser //
- //////////////////////
-
- /*!
- @brief lexical analysis
-
- This class organizes the lexical analysis during JSON deserialization. The
- core of it is a scanner generated by [re2c](http://re2c.org) that
- processes a buffer and recognizes tokens according to RFC 7159.
- */
- class lexer
- {
- public:
- /// token types for the parser
- enum class token_type
- {
- uninitialized, ///< indicating the scanner is uninitialized
- literal_true, ///< the `true` literal
- literal_false, ///< the `false` literal
- literal_null, ///< the `null` literal
- value_string, ///< a string -- use get_string() for actual value
- value_number, ///< a number -- use get_number() for actual value
- begin_array, ///< the character for array begin `[`
- begin_object, ///< the character for object begin `{`
- end_array, ///< the character for array end `]`
- end_object, ///< the character for object end `}`
- name_separator, ///< the name separator `:`
- value_separator, ///< the value separator `,`
- parse_error, ///< indicating a parse error
- end_of_input ///< indicating the end of the input buffer
- };
-
- /// the char type to use in the lexer
- using lexer_char_t = unsigned char;
-
- /// a lexer from a buffer with given length
- lexer(const lexer_char_t* buff, const size_t len) noexcept
- : m_content(buff)
- {
- assert(m_content != nullptr);
- m_start = m_cursor = m_content;
- m_limit = m_content + len;
- }
-
- /// a lexer from an input stream
- explicit lexer(std::istream& s)
- : m_stream(&s), m_line_buffer()
- {
- // immediately abort if stream is erroneous
- if (s.fail())
- {
- throw std::invalid_argument("stream error");
- }
-
- // fill buffer
- fill_line_buffer();
-
- // skip UTF-8 byte-order mark
- if (m_line_buffer.size() >= 3 and m_line_buffer.substr(0, 3) == "\xEF\xBB\xBF")
- {
- m_line_buffer[0] = ' ';
- m_line_buffer[1] = ' ';
- m_line_buffer[2] = ' ';
- }
- }
-
- // switch off unwanted functions (due to pointer members)
- lexer() = delete;
- lexer(const lexer&) = delete;
- lexer operator=(const lexer&) = delete;
-
- /*!
- @brief create a string from one or two Unicode code points
-
- There are two cases: (1) @a codepoint1 is in the Basic Multilingual
- Plane (U+0000 through U+FFFF) and @a codepoint2 is 0, or (2)
- @a codepoint1 and @a codepoint2 are a UTF-16 surrogate pair to
- represent a code point above U+FFFF.
-
- @param[in] codepoint1 the code point (can be high surrogate)
- @param[in] codepoint2 the code point (can be low surrogate or 0)
-
- @return string representation of the code point; the length of the
- result string is between 1 and 4 characters.
-
- @throw std::out_of_range if code point is > 0x10ffff; example: `"code
- points above 0x10FFFF are invalid"`
- @throw std::invalid_argument if the low surrogate is invalid; example:
- `""missing or wrong low surrogate""`
-
- @complexity Constant.
-
- @see <http://en.wikipedia.org/wiki/UTF-8#Sample_code>
- */
- static string_t to_unicode(const std::size_t codepoint1,
- const std::size_t codepoint2 = 0)
- {
- // calculate the code point from the given code points
- std::size_t codepoint = codepoint1;
-
- // check if codepoint1 is a high surrogate
- if (codepoint1 >= 0xD800 and codepoint1 <= 0xDBFF)
- {
- // check if codepoint2 is a low surrogate
- if (codepoint2 >= 0xDC00 and codepoint2 <= 0xDFFF)
- {
- codepoint =
- // high surrogate occupies the most significant 22 bits
- (codepoint1 << 10)
- // low surrogate occupies the least significant 15 bits
- + codepoint2
- // there is still the 0xD800, 0xDC00 and 0x10000 noise
- // in the result so we have to subtract with:
- // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00
- - 0x35FDC00;
- }
- else
- {
- throw std::invalid_argument("missing or wrong low surrogate");
- }
- }
-
- string_t result;
-
- if (codepoint < 0x80)
- {
- // 1-byte characters: 0xxxxxxx (ASCII)
- result.append(1, static_cast<typename string_t::value_type>(codepoint));
- }
- else if (codepoint <= 0x7ff)
- {
- // 2-byte characters: 110xxxxx 10xxxxxx
- result.append(1, static_cast<typename string_t::value_type>(0xC0 | ((codepoint >> 6) & 0x1F)));
- result.append(1, static_cast<typename string_t::value_type>(0x80 | (codepoint & 0x3F)));
- }
- else if (codepoint <= 0xffff)
- {
- // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx
- result.append(1, static_cast<typename string_t::value_type>(0xE0 | ((codepoint >> 12) & 0x0F)));
- result.append(1, static_cast<typename string_t::value_type>(0x80 | ((codepoint >> 6) & 0x3F)));
- result.append(1, static_cast<typename string_t::value_type>(0x80 | (codepoint & 0x3F)));
- }
- else if (codepoint <= 0x10ffff)
- {
- // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
- result.append(1, static_cast<typename string_t::value_type>(0xF0 | ((codepoint >> 18) & 0x07)));
- result.append(1, static_cast<typename string_t::value_type>(0x80 | ((codepoint >> 12) & 0x3F)));
- result.append(1, static_cast<typename string_t::value_type>(0x80 | ((codepoint >> 6) & 0x3F)));
- result.append(1, static_cast<typename string_t::value_type>(0x80 | (codepoint & 0x3F)));
- }
- else
- {
- throw std::out_of_range("code points above 0x10FFFF are invalid");
- }
-
- return result;
- }
-
- /// return name of values of type token_type (only used for errors)
- static std::string token_type_name(const token_type t)
- {
- switch (t)
- {
- case token_type::uninitialized:
- return "<uninitialized>";
- case token_type::literal_true:
- return "true literal";
- case token_type::literal_false:
- return "false literal";
- case token_type::literal_null:
- return "null literal";
- case token_type::value_string:
- return "string literal";
- case token_type::value_number:
- return "number literal";
- case token_type::begin_array:
- return "'['";
- case token_type::begin_object:
- return "'{'";
- case token_type::end_array:
- return "']'";
- case token_type::end_object:
- return "'}'";
- case token_type::name_separator:
- return "':'";
- case token_type::value_separator:
- return "','";
- case token_type::parse_error:
- return "<parse error>";
- case token_type::end_of_input:
- return "end of input";
- default:
- {
- // catch non-enum values
- return "unknown token"; // LCOV_EXCL_LINE
- }
- }
- }
-
- /*!
- This function implements a scanner for JSON. It is specified using
- regular expressions that try to follow RFC 7159 as close as possible.
- These regular expressions are then translated into a minimized
- deterministic finite automaton (DFA) by the tool
- [re2c](http://re2c.org). As a result, the translated code for this
- function consists of a large block of code with `goto` jumps.
-
- @return the class of the next token read from the buffer
-
- @complexity Linear in the length of the input.\n
-
- Proposition: The loop below will always terminate for finite input.\n
-
- Proof (by contradiction): Assume a finite input. To loop forever, the
- loop must never hit code with a `break` statement. The only code
- snippets without a `break` statement are the continue statements for
- whitespace and byte-order-marks. To loop forever, the input must be an
- infinite sequence of whitespace or byte-order-marks. This contradicts
- the assumption of finite input, q.e.d.
- */
- token_type scan()
- {
- while (true)
- {
- // pointer for backtracking information
- m_marker = nullptr;
-
- // remember the begin of the token
- m_start = m_cursor;
- assert(m_start != nullptr);
-
-
- {
- lexer_char_t yych;
- unsigned int yyaccept = 0;
- static const unsigned char yybm[] =
- {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 32, 32, 0, 0, 32, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 160, 128, 0, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 192, 192, 192, 192, 192, 192, 192, 192,
- 192, 192, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 0, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- };
- if ((m_limit - m_cursor) < 5)
- {
- fill_line_buffer(5); // LCOV_EXCL_LINE
- }
- yych = *m_cursor;
- if (yybm[0 + yych] & 32)
- {
- goto basic_json_parser_6;
- }
- if (yych <= '[')
- {
- if (yych <= '-')
- {
- if (yych <= '"')
- {
- if (yych <= 0x00)
- {
- goto basic_json_parser_2;
- }
- if (yych <= '!')
- {
- goto basic_json_parser_4;
- }
- goto basic_json_parser_9;
- }
- else
- {
- if (yych <= '+')
- {
- goto basic_json_parser_4;
- }
- if (yych <= ',')
- {
- goto basic_json_parser_10;
- }
- goto basic_json_parser_12;
- }
- }
- else
- {
- if (yych <= '9')
- {
- if (yych <= '/')
- {
- goto basic_json_parser_4;
- }
- if (yych <= '0')
- {
- goto basic_json_parser_13;
- }
- goto basic_json_parser_15;
- }
- else
- {
- if (yych <= ':')
- {
- goto basic_json_parser_17;
- }
- if (yych <= 'Z')
- {
- goto basic_json_parser_4;
- }
- goto basic_json_parser_19;
- }
- }
- }
- else
- {
- if (yych <= 'n')
- {
- if (yych <= 'e')
- {
- if (yych == ']')
- {
- goto basic_json_parser_21;
- }
- goto basic_json_parser_4;
- }
- else
- {
- if (yych <= 'f')
- {
- goto basic_json_parser_23;
- }
- if (yych <= 'm')
- {
- goto basic_json_parser_4;
- }
- goto basic_json_parser_24;
- }
- }
- else
- {
- if (yych <= 'z')
- {
- if (yych == 't')
- {
- goto basic_json_parser_25;
- }
- goto basic_json_parser_4;
- }
- else
- {
- if (yych <= '{')
- {
- goto basic_json_parser_26;
- }
- if (yych == '}')
- {
- goto basic_json_parser_28;
- }
- goto basic_json_parser_4;
- }
- }
- }
-basic_json_parser_2:
- ++m_cursor;
- {
- last_token_type = token_type::end_of_input;
- break;
- }
-basic_json_parser_4:
- ++m_cursor;
-basic_json_parser_5:
- {
- last_token_type = token_type::parse_error;
- break;
- }
-basic_json_parser_6:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- fill_line_buffer(1); // LCOV_EXCL_LINE
- }
- yych = *m_cursor;
- if (yybm[0 + yych] & 32)
- {
- goto basic_json_parser_6;
- }
- {
- continue;
- }
-basic_json_parser_9:
- yyaccept = 0;
- yych = *(m_marker = ++m_cursor);
- if (yych <= 0x1F)
- {
- goto basic_json_parser_5;
- }
- if (yych <= 0x7F)
- {
- goto basic_json_parser_31;
- }
- if (yych <= 0xC1)
- {
- goto basic_json_parser_5;
- }
- if (yych <= 0xF4)
- {
- goto basic_json_parser_31;
- }
- goto basic_json_parser_5;
-basic_json_parser_10:
- ++m_cursor;
- {
- last_token_type = token_type::value_separator;
- break;
- }
-basic_json_parser_12:
- yych = *++m_cursor;
- if (yych <= '/')
- {
- goto basic_json_parser_5;
- }
- if (yych <= '0')
- {
- goto basic_json_parser_13;
- }
- if (yych <= '9')
- {
- goto basic_json_parser_15;
- }
- goto basic_json_parser_5;
-basic_json_parser_13:
- yyaccept = 1;
- yych = *(m_marker = ++m_cursor);
- if (yych <= 'D')
- {
- if (yych == '.')
- {
- goto basic_json_parser_43;
- }
- }
- else
- {
- if (yych <= 'E')
- {
- goto basic_json_parser_44;
- }
- if (yych == 'e')
- {
- goto basic_json_parser_44;
- }
- }
-basic_json_parser_14:
- {
- last_token_type = token_type::value_number;
- break;
- }
-basic_json_parser_15:
- yyaccept = 1;
- m_marker = ++m_cursor;
- if ((m_limit - m_cursor) < 3)
- {
- fill_line_buffer(3); // LCOV_EXCL_LINE
- }
- yych = *m_cursor;
- if (yybm[0 + yych] & 64)
- {
- goto basic_json_parser_15;
- }
- if (yych <= 'D')
- {
- if (yych == '.')
- {
- goto basic_json_parser_43;
- }
- goto basic_json_parser_14;
- }
- else
- {
- if (yych <= 'E')
- {
- goto basic_json_parser_44;
- }
- if (yych == 'e')
- {
- goto basic_json_parser_44;
- }
- goto basic_json_parser_14;
- }
-basic_json_parser_17:
- ++m_cursor;
- {
- last_token_type = token_type::name_separator;
- break;
- }
-basic_json_parser_19:
- ++m_cursor;
- {
- last_token_type = token_type::begin_array;
- break;
- }
-basic_json_parser_21:
- ++m_cursor;
- {
- last_token_type = token_type::end_array;
- break;
- }
-basic_json_parser_23:
- yyaccept = 0;
- yych = *(m_marker = ++m_cursor);
- if (yych == 'a')
- {
- goto basic_json_parser_45;
- }
- goto basic_json_parser_5;
-basic_json_parser_24:
- yyaccept = 0;
- yych = *(m_marker = ++m_cursor);
- if (yych == 'u')
- {
- goto basic_json_parser_46;
- }
- goto basic_json_parser_5;
-basic_json_parser_25:
- yyaccept = 0;
- yych = *(m_marker = ++m_cursor);
- if (yych == 'r')
- {
- goto basic_json_parser_47;
- }
- goto basic_json_parser_5;
-basic_json_parser_26:
- ++m_cursor;
- {
- last_token_type = token_type::begin_object;
- break;
- }
-basic_json_parser_28:
- ++m_cursor;
- {
- last_token_type = token_type::end_object;
- break;
- }
-basic_json_parser_30:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- fill_line_buffer(1); // LCOV_EXCL_LINE
- }
- yych = *m_cursor;
-basic_json_parser_31:
- if (yybm[0 + yych] & 128)
- {
- goto basic_json_parser_30;
- }
- if (yych <= 0xE0)
- {
- if (yych <= '\\')
- {
- if (yych <= 0x1F)
- {
- goto basic_json_parser_32;
- }
- if (yych <= '"')
- {
- goto basic_json_parser_33;
- }
- goto basic_json_parser_35;
- }
- else
- {
- if (yych <= 0xC1)
- {
- goto basic_json_parser_32;
- }
- if (yych <= 0xDF)
- {
- goto basic_json_parser_36;
- }
- goto basic_json_parser_37;
- }
- }
- else
- {
- if (yych <= 0xEF)
- {
- if (yych == 0xED)
- {
- goto basic_json_parser_39;
- }
- goto basic_json_parser_38;
- }
- else
- {
- if (yych <= 0xF0)
- {
- goto basic_json_parser_40;
- }
- if (yych <= 0xF3)
- {
- goto basic_json_parser_41;
- }
- if (yych <= 0xF4)
- {
- goto basic_json_parser_42;
- }
- }
- }
-basic_json_parser_32:
- m_cursor = m_marker;
- if (yyaccept == 0)
- {
- goto basic_json_parser_5;
- }
- else
- {
- goto basic_json_parser_14;
- }
-basic_json_parser_33:
- ++m_cursor;
- {
- last_token_type = token_type::value_string;
- break;
- }
-basic_json_parser_35:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- fill_line_buffer(1); // LCOV_EXCL_LINE
- }
- yych = *m_cursor;
- if (yych <= 'e')
- {
- if (yych <= '/')
- {
- if (yych == '"')
- {
- goto basic_json_parser_30;
- }
- if (yych <= '.')
- {
- goto basic_json_parser_32;
- }
- goto basic_json_parser_30;
- }
- else
- {
- if (yych <= '\\')
- {
- if (yych <= '[')
- {
- goto basic_json_parser_32;
- }
- goto basic_json_parser_30;
- }
- else
- {
- if (yych == 'b')
- {
- goto basic_json_parser_30;
- }
- goto basic_json_parser_32;
- }
- }
- }
- else
- {
- if (yych <= 'q')
- {
- if (yych <= 'f')
- {
- goto basic_json_parser_30;
- }
- if (yych == 'n')
- {
- goto basic_json_parser_30;
- }
- goto basic_json_parser_32;
- }
- else
- {
- if (yych <= 's')
- {
- if (yych <= 'r')
- {
- goto basic_json_parser_30;
- }
- goto basic_json_parser_32;
- }
- else
- {
- if (yych <= 't')
- {
- goto basic_json_parser_30;
- }
- if (yych <= 'u')
- {
- goto basic_json_parser_48;
- }
- goto basic_json_parser_32;
- }
- }
- }
-basic_json_parser_36:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- fill_line_buffer(1); // LCOV_EXCL_LINE
- }
- yych = *m_cursor;
- if (yych <= 0x7F)
- {
- goto basic_json_parser_32;
- }
- if (yych <= 0xBF)
- {
- goto basic_json_parser_30;
- }
- goto basic_json_parser_32;
-basic_json_parser_37:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- fill_line_buffer(1); // LCOV_EXCL_LINE
- }
- yych = *m_cursor;
- if (yych <= 0x9F)
- {
- goto basic_json_parser_32;
- }
- if (yych <= 0xBF)
- {
- goto basic_json_parser_36;
- }
- goto basic_json_parser_32;
-basic_json_parser_38:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- fill_line_buffer(1); // LCOV_EXCL_LINE
- }
- yych = *m_cursor;
- if (yych <= 0x7F)
- {
- goto basic_json_parser_32;
- }
- if (yych <= 0xBF)
- {
- goto basic_json_parser_36;
- }
- goto basic_json_parser_32;
-basic_json_parser_39:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- fill_line_buffer(1); // LCOV_EXCL_LINE
- }
- yych = *m_cursor;
- if (yych <= 0x7F)
- {
- goto basic_json_parser_32;
- }
- if (yych <= 0x9F)
- {
- goto basic_json_parser_36;
- }
- goto basic_json_parser_32;
-basic_json_parser_40:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- fill_line_buffer(1); // LCOV_EXCL_LINE
- }
- yych = *m_cursor;
- if (yych <= 0x8F)
- {
- goto basic_json_parser_32;
- }
- if (yych <= 0xBF)
- {
- goto basic_json_parser_38;
- }
- goto basic_json_parser_32;
-basic_json_parser_41:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- fill_line_buffer(1); // LCOV_EXCL_LINE
- }
- yych = *m_cursor;
- if (yych <= 0x7F)
- {
- goto basic_json_parser_32;
- }
- if (yych <= 0xBF)
- {
- goto basic_json_parser_38;
- }
- goto basic_json_parser_32;
-basic_json_parser_42:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- fill_line_buffer(1); // LCOV_EXCL_LINE
- }
- yych = *m_cursor;
- if (yych <= 0x7F)
- {
- goto basic_json_parser_32;
- }
- if (yych <= 0x8F)
- {
- goto basic_json_parser_38;
- }
- goto basic_json_parser_32;
-basic_json_parser_43:
- yych = *++m_cursor;
- if (yych <= '/')
- {
- goto basic_json_parser_32;
- }
- if (yych <= '9')
- {
- goto basic_json_parser_49;
- }
- goto basic_json_parser_32;
-basic_json_parser_44:
- yych = *++m_cursor;
- if (yych <= ',')
- {
- if (yych == '+')
- {
- goto basic_json_parser_51;
- }
- goto basic_json_parser_32;
- }
- else
- {
- if (yych <= '-')
- {
- goto basic_json_parser_51;
- }
- if (yych <= '/')
- {
- goto basic_json_parser_32;
- }
- if (yych <= '9')
- {
- goto basic_json_parser_52;
- }
- goto basic_json_parser_32;
- }
-basic_json_parser_45:
- yych = *++m_cursor;
- if (yych == 'l')
- {
- goto basic_json_parser_54;
- }
- goto basic_json_parser_32;
-basic_json_parser_46:
- yych = *++m_cursor;
- if (yych == 'l')
- {
- goto basic_json_parser_55;
- }
- goto basic_json_parser_32;
-basic_json_parser_47:
- yych = *++m_cursor;
- if (yych == 'u')
- {
- goto basic_json_parser_56;
- }
- goto basic_json_parser_32;
-basic_json_parser_48:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- fill_line_buffer(1); // LCOV_EXCL_LINE
- }
- yych = *m_cursor;
- if (yych <= '@')
- {
- if (yych <= '/')
- {
- goto basic_json_parser_32;
- }
- if (yych <= '9')
- {
- goto basic_json_parser_57;
- }
- goto basic_json_parser_32;
- }
- else
- {
- if (yych <= 'F')
- {
- goto basic_json_parser_57;
- }
- if (yych <= '`')
- {
- goto basic_json_parser_32;
- }
- if (yych <= 'f')
- {
- goto basic_json_parser_57;
- }
- goto basic_json_parser_32;
- }
-basic_json_parser_49:
- yyaccept = 1;
- m_marker = ++m_cursor;
- if ((m_limit - m_cursor) < 3)
- {
- fill_line_buffer(3); // LCOV_EXCL_LINE
- }
- yych = *m_cursor;
- if (yych <= 'D')
- {
- if (yych <= '/')
- {
- goto basic_json_parser_14;
- }
- if (yych <= '9')
- {
- goto basic_json_parser_49;
- }
- goto basic_json_parser_14;
- }
- else
- {
- if (yych <= 'E')
- {
- goto basic_json_parser_44;
- }
- if (yych == 'e')
- {
- goto basic_json_parser_44;
- }
- goto basic_json_parser_14;
- }
-basic_json_parser_51:
- yych = *++m_cursor;
- if (yych <= '/')
- {
- goto basic_json_parser_32;
- }
- if (yych >= ':')
- {
- goto basic_json_parser_32;
- }
-basic_json_parser_52:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- fill_line_buffer(1); // LCOV_EXCL_LINE
- }
- yych = *m_cursor;
- if (yych <= '/')
- {
- goto basic_json_parser_14;
- }
- if (yych <= '9')
- {
- goto basic_json_parser_52;
- }
- goto basic_json_parser_14;
-basic_json_parser_54:
- yych = *++m_cursor;
- if (yych == 's')
- {
- goto basic_json_parser_58;
- }
- goto basic_json_parser_32;
-basic_json_parser_55:
- yych = *++m_cursor;
- if (yych == 'l')
- {
- goto basic_json_parser_59;
- }
- goto basic_json_parser_32;
-basic_json_parser_56:
- yych = *++m_cursor;
- if (yych == 'e')
- {
- goto basic_json_parser_61;
- }
- goto basic_json_parser_32;
-basic_json_parser_57:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- fill_line_buffer(1); // LCOV_EXCL_LINE
- }
- yych = *m_cursor;
- if (yych <= '@')
- {
- if (yych <= '/')
- {
- goto basic_json_parser_32;
- }
- if (yych <= '9')
- {
- goto basic_json_parser_63;
- }
- goto basic_json_parser_32;
- }
- else
- {
- if (yych <= 'F')
- {
- goto basic_json_parser_63;
- }
- if (yych <= '`')
- {
- goto basic_json_parser_32;
- }
- if (yych <= 'f')
- {
- goto basic_json_parser_63;
- }
- goto basic_json_parser_32;
- }
-basic_json_parser_58:
- yych = *++m_cursor;
- if (yych == 'e')
- {
- goto basic_json_parser_64;
- }
- goto basic_json_parser_32;
-basic_json_parser_59:
- ++m_cursor;
- {
- last_token_type = token_type::literal_null;
- break;
- }
-basic_json_parser_61:
- ++m_cursor;
- {
- last_token_type = token_type::literal_true;
- break;
- }
-basic_json_parser_63:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- fill_line_buffer(1); // LCOV_EXCL_LINE
- }
- yych = *m_cursor;
- if (yych <= '@')
- {
- if (yych <= '/')
- {
- goto basic_json_parser_32;
- }
- if (yych <= '9')
- {
- goto basic_json_parser_66;
- }
- goto basic_json_parser_32;
- }
- else
- {
- if (yych <= 'F')
- {
- goto basic_json_parser_66;
- }
- if (yych <= '`')
- {
- goto basic_json_parser_32;
- }
- if (yych <= 'f')
- {
- goto basic_json_parser_66;
- }
- goto basic_json_parser_32;
- }
-basic_json_parser_64:
- ++m_cursor;
- {
- last_token_type = token_type::literal_false;
- break;
- }
-basic_json_parser_66:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- fill_line_buffer(1); // LCOV_EXCL_LINE
- }
- yych = *m_cursor;
- if (yych <= '@')
- {
- if (yych <= '/')
- {
- goto basic_json_parser_32;
- }
- if (yych <= '9')
- {
- goto basic_json_parser_30;
- }
- goto basic_json_parser_32;
- }
- else
- {
- if (yych <= 'F')
- {
- goto basic_json_parser_30;
- }
- if (yych <= '`')
- {
- goto basic_json_parser_32;
- }
- if (yych <= 'f')
- {
- goto basic_json_parser_30;
- }
- goto basic_json_parser_32;
- }
- }
-
- }
-
- return last_token_type;
- }
-
- /*!
- @brief append data from the stream to the line buffer
-
- This function is called by the scan() function when the end of the
- buffer (`m_limit`) is reached and the `m_cursor` pointer cannot be
- incremented without leaving the limits of the line buffer. Note re2c
- decides when to call this function.
-
- If the lexer reads from contiguous storage, there is no trailing null
- byte. Therefore, this function must make sure to add these padding
- null bytes.
-
- If the lexer reads from an input stream, this function reads the next
- line of the input.
-
- @pre
- p p p p p p u u u u u x . . . . . .
- ^ ^ ^ ^
- m_content m_start | m_limit
- m_cursor
-
- @post
- u u u u u x x x x x x x . . . . . .
- ^ ^ ^
- | m_cursor m_limit
- m_start
- m_content
- */
- void fill_line_buffer(size_t n = 0)
- {
- // if line buffer is used, m_content points to its data
- assert(m_line_buffer.empty()
- or m_content == reinterpret_cast<const lexer_char_t*>(m_line_buffer.data()));
-
- // if line buffer is used, m_limit is set past the end of its data
- assert(m_line_buffer.empty()
- or m_limit == m_content + m_line_buffer.size());
-
- // pointer relationships
- assert(m_content <= m_start);
- assert(m_start <= m_cursor);
- assert(m_cursor <= m_limit);
- assert(m_marker == nullptr or m_marker <= m_limit);
-
- // number of processed characters (p)
- const size_t num_processed_chars = static_cast<size_t>(m_start - m_content);
- // offset for m_marker wrt. to m_start
- const auto offset_marker = (m_marker == nullptr) ? 0 : m_marker - m_start;
- // number of unprocessed characters (u)
- const auto offset_cursor = m_cursor - m_start;
-
- // no stream is used or end of file is reached
- if (m_stream == nullptr or m_stream->eof())
- {
- // m_start may or may not be pointing into m_line_buffer at
- // this point. We trust the standand library to do the right
- // thing. See http://stackoverflow.com/q/28142011/266378
- m_line_buffer.assign(m_start, m_limit);
-
- // append n characters to make sure that there is sufficient
- // space between m_cursor and m_limit
- m_line_buffer.append(1, '\x00');
- if (n > 0)
- {
- m_line_buffer.append(n - 1, '\x01');
- }
- }
- else
- {
- // delete processed characters from line buffer
- m_line_buffer.erase(0, num_processed_chars);
- // read next line from input stream
- m_line_buffer_tmp.clear();
- std::getline(*m_stream, m_line_buffer_tmp, '\n');
-
- // add line with newline symbol to the line buffer
- m_line_buffer += m_line_buffer_tmp;
- m_line_buffer.push_back('\n');
- }
-
- // set pointers
- m_content = reinterpret_cast<const lexer_char_t*>(m_line_buffer.data());
- assert(m_content != nullptr);
- m_start = m_content;
- m_marker = m_start + offset_marker;
- m_cursor = m_start + offset_cursor;
- m_limit = m_start + m_line_buffer.size();
- }
-
- /// return string representation of last read token
- string_t get_token_string() const
- {
- assert(m_start != nullptr);
- return string_t(reinterpret_cast<typename string_t::const_pointer>(m_start),
- static_cast<size_t>(m_cursor - m_start));
- }
-
- /*!
- @brief return string value for string tokens
-
- The function iterates the characters between the opening and closing
- quotes of the string value. The complete string is the range
- [m_start,m_cursor). Consequently, we iterate from m_start+1 to
- m_cursor-1.
-
- We differentiate two cases:
-
- 1. Escaped characters. In this case, a new character is constructed
- according to the nature of the escape. Some escapes create new
- characters (e.g., `"\\n"` is replaced by `"\n"`), some are copied
- as is (e.g., `"\\\\"`). Furthermore, Unicode escapes of the shape
- `"\\uxxxx"` need special care. In this case, to_unicode takes care
- of the construction of the values.
- 2. Unescaped characters are copied as is.
-
- @pre `m_cursor - m_start >= 2`, meaning the length of the last token
- is at least 2 bytes which is trivially true for any string (which
- consists of at least two quotes).
-
- " c1 c2 c3 ... "
- ^ ^
- m_start m_cursor
-
- @complexity Linear in the length of the string.\n
-
- Lemma: The loop body will always terminate.\n
-
- Proof (by contradiction): Assume the loop body does not terminate. As
- the loop body does not contain another loop, one of the called
- functions must never return. The called functions are `std::strtoul`
- and to_unicode. Neither function can loop forever, so the loop body
- will never loop forever which contradicts the assumption that the loop
- body does not terminate, q.e.d.\n
-
- Lemma: The loop condition for the for loop is eventually false.\n
-
- Proof (by contradiction): Assume the loop does not terminate. Due to
- the above lemma, this can only be due to a tautological loop
- condition; that is, the loop condition i < m_cursor - 1 must always be
- true. Let x be the change of i for any loop iteration. Then
- m_start + 1 + x < m_cursor - 1 must hold to loop indefinitely. This
- can be rephrased to m_cursor - m_start - 2 > x. With the
- precondition, we x <= 0, meaning that the loop condition holds
- indefinitly if i is always decreased. However, observe that the value
- of i is strictly increasing with each iteration, as it is incremented
- by 1 in the iteration expression and never decremented inside the loop
- body. Hence, the loop condition will eventually be false which
- contradicts the assumption that the loop condition is a tautology,
- q.e.d.
-
- @return string value of current token without opening and closing
- quotes
- @throw std::out_of_range if to_unicode fails
- */
- string_t get_string() const
- {
- assert(m_cursor - m_start >= 2);
-
- string_t result;
- result.reserve(static_cast<size_t>(m_cursor - m_start - 2));
-
- // iterate the result between the quotes
- for (const lexer_char_t* i = m_start + 1; i < m_cursor - 1; ++i)
- {
- // find next escape character
- auto e = std::find(i, m_cursor - 1, '\\');
- if (e != i)
- {
- // see https://github.com/nlohmann/json/issues/365#issuecomment-262874705
- for (auto k = i; k < e; k++)
- {
- result.push_back(static_cast<typename string_t::value_type>(*k));
- }
- i = e - 1; // -1 because of ++i
- }
- else
- {
- // processing escaped character
- // read next character
- ++i;
-
- switch (*i)
- {
- // the default escapes
- case 't':
- {
- result += "\t";
- break;
- }
- case 'b':
- {
- result += "\b";
- break;
- }
- case 'f':
- {
- result += "\f";
- break;
- }
- case 'n':
- {
- result += "\n";
- break;
- }
- case 'r':
- {
- result += "\r";
- break;
- }
- case '\\':
- {
- result += "\\";
- break;
- }
- case '/':
- {
- result += "/";
- break;
- }
- case '"':
- {
- result += "\"";
- break;
- }
-
- // unicode
- case 'u':
- {
- // get code xxxx from uxxxx
- auto codepoint = std::strtoul(std::string(reinterpret_cast<typename string_t::const_pointer>(i + 1),
- 4).c_str(), nullptr, 16);
-
- // check if codepoint is a high surrogate
- if (codepoint >= 0xD800 and codepoint <= 0xDBFF)
- {
- // make sure there is a subsequent unicode
- if ((i + 6 >= m_limit) or * (i + 5) != '\\' or * (i + 6) != 'u')
- {
- throw std::invalid_argument("missing low surrogate");
- }
-
- // get code yyyy from uxxxx\uyyyy
- auto codepoint2 = std::strtoul(std::string(reinterpret_cast<typename string_t::const_pointer>
- (i + 7), 4).c_str(), nullptr, 16);
- result += to_unicode(codepoint, codepoint2);
- // skip the next 10 characters (xxxx\uyyyy)
- i += 10;
- }
- else if (codepoint >= 0xDC00 and codepoint <= 0xDFFF)
- {
- // we found a lone low surrogate
- throw std::invalid_argument("missing high surrogate");
- }
- else
- {
- // add unicode character(s)
- result += to_unicode(codepoint);
- // skip the next four characters (xxxx)
- i += 4;
- }
- break;
- }
- }
- }
- }
-
- return result;
- }
-
- /*!
- @brief parse floating point number
-
- This function (and its overloads) serves to select the most approprate
- standard floating point number parsing function based on the type
- supplied via the first parameter. Set this to @a
- static_cast<number_float_t*>(nullptr).
-
- @param[in,out] endptr recieves a pointer to the first character after
- the number
-
- @return the floating point number
- */
- long double str_to_float_t(long double* /* type */, char** endptr) const
- {
- return std::strtold(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr);
- }
-
- /*!
- @brief parse floating point number
-
- This function (and its overloads) serves to select the most approprate
- standard floating point number parsing function based on the type
- supplied via the first parameter. Set this to @a
- static_cast<number_float_t*>(nullptr).
-
- @param[in,out] endptr recieves a pointer to the first character after
- the number
-
- @return the floating point number
- */
- double str_to_float_t(double* /* type */, char** endptr) const
- {
- return std::strtod(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr);
- }
-
- /*!
- @brief parse floating point number
-
- This function (and its overloads) serves to select the most approprate
- standard floating point number parsing function based on the type
- supplied via the first parameter. Set this to @a
- static_cast<number_float_t*>(nullptr).
-
- @param[in,out] endptr recieves a pointer to the first character after
- the number
-
- @return the floating point number
- */
- float str_to_float_t(float* /* type */, char** endptr) const
- {
- return std::strtof(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr);
- }
-
- /*!
- @brief return number value for number tokens
-
- This function translates the last token into the most appropriate
- number type (either integer, unsigned integer or floating point),
- which is passed back to the caller via the result parameter.
-
- This function parses the integer component up to the radix point or
- exponent while collecting information about the 'floating point
- representation', which it stores in the result parameter. If there is
- no radix point or exponent, and the number can fit into a @ref
- number_integer_t or @ref number_unsigned_t then it sets the result
- parameter accordingly.
-
- If the number is a floating point number the number is then parsed
- using @a std:strtod (or @a std:strtof or @a std::strtold).
-
- @param[out] result @ref basic_json object to receive the number, or
- NAN if the conversion read past the current token. The latter case
- needs to be treated by the caller function.
- */
- void get_number(basic_json& result) const
- {
- assert(m_start != nullptr);
-
- const lexer::lexer_char_t* curptr = m_start;
-
- // accumulate the integer conversion result (unsigned for now)
- number_unsigned_t value = 0;
-
- // maximum absolute value of the relevant integer type
- number_unsigned_t max;
-
- // temporarily store the type to avoid unecessary bitfield access
- value_t type;
-
- // look for sign
- if (*curptr == '-')
- {
- type = value_t::number_integer;
- max = static_cast<uint64_t>((std::numeric_limits<number_integer_t>::max)()) + 1;
- curptr++;
- }
- else
- {
- type = value_t::number_unsigned;
- max = static_cast<uint64_t>((std::numeric_limits<number_unsigned_t>::max)());
- }
-
- // count the significant figures
- for (; curptr < m_cursor; curptr++)
- {
- // quickly skip tests if a digit
- if (*curptr < '0' || *curptr > '9')
- {
- if (*curptr == '.')
- {
- // don't count '.' but change to float
- type = value_t::number_float;
- continue;
- }
- // assume exponent (if not then will fail parse): change to
- // float, stop counting and record exponent details
- type = value_t::number_float;
- break;
- }
-
- // skip if definitely not an integer
- if (type != value_t::number_float)
- {
- auto digit = static_cast<number_unsigned_t>(*curptr - '0');
-
- // overflow if value * 10 + digit > max, move terms around
- // to avoid overflow in intermediate values
- if (value > (max - digit) / 10)
- {
- // overflow
- type = value_t::number_float;
- }
- else
- {
- // no overflow
- value = value * 10 + digit;
- }
- }
- }
-
- // save the value (if not a float)
- if (type == value_t::number_unsigned)
- {
- result.m_value.number_unsigned = value;
- }
- else if (type == value_t::number_integer)
- {
- // invariant: if we parsed a '-', the absolute value is between
- // 0 (we allow -0) and max == -INT64_MIN
- assert(value >= 0);
- assert(value <= max);
-
- if (value == max)
- {
- // we cannot simply negate value (== max == -INT64_MIN),
- // see https://github.com/nlohmann/json/issues/389
- result.m_value.number_integer = static_cast<number_integer_t>(INT64_MIN);
- }
- else
- {
- // all other values can be negated safely
- result.m_value.number_integer = -static_cast<number_integer_t>(value);
- }
- }
- else
- {
- // parse with strtod
- result.m_value.number_float = str_to_float_t(static_cast<number_float_t*>(nullptr), NULL);
-
- // replace infinity and NAN by null
- if (not std::isfinite(result.m_value.number_float))
- {
- type = value_t::null;
- result.m_value = basic_json::json_value();
- }
- }
-
- // save the type
- result.m_type = type;
- }
-
- private:
- /// optional input stream
- std::istream* m_stream = nullptr;
- /// line buffer buffer for m_stream
- string_t m_line_buffer {};
- /// used for filling m_line_buffer
- string_t m_line_buffer_tmp {};
- /// the buffer pointer
- const lexer_char_t* m_content = nullptr;
- /// pointer to the beginning of the current symbol
- const lexer_char_t* m_start = nullptr;
- /// pointer for backtracking information
- const lexer_char_t* m_marker = nullptr;
- /// pointer to the current symbol
- const lexer_char_t* m_cursor = nullptr;
- /// pointer to the end of the buffer
- const lexer_char_t* m_limit = nullptr;
- /// the last token type
- token_type last_token_type = token_type::end_of_input;
- };
-
/*!
- @brief syntax analysis
-
- This class implements a recursive decent parser.
+ @copydoc from_msgpack(detail::input_adapter, const bool)
*/
- class parser
+ template<typename A1, typename A2,
+ detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>
+ static basic_json from_msgpack(A1 && a1, A2 && a2, const bool strict = true)
{
- public:
- /// a parser reading from a string literal
- parser(const char* buff, const parser_callback_t cb = nullptr)
- : callback(cb),
- m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(buff), std::strlen(buff))
- {}
-
- /// a parser reading from an input stream
- parser(std::istream& is, const parser_callback_t cb = nullptr)
- : callback(cb), m_lexer(is)
- {}
-
- /// a parser reading from an iterator range with contiguous storage
- template<class IteratorType, typename std::enable_if<
- std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value
- , int>::type
- = 0>
- parser(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr)
- : callback(cb),
- m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(&(*first)),
- static_cast<size_t>(std::distance(first, last)))
- {}
-
- /// public parser interface
- basic_json parse()
- {
- // read first token
- get_token();
-
- basic_json result = parse_internal(true);
- result.assert_invariant();
-
- expect(lexer::token_type::end_of_input);
-
- // return parser result and replace it with null in case the
- // top-level value was discarded by the callback function
- return result.is_discarded() ? basic_json() : std::move(result);
- }
-
- private:
- /// the actual parser
- basic_json parse_internal(bool keep)
- {
- auto result = basic_json(value_t::discarded);
-
- switch (last_token)
- {
- case lexer::token_type::begin_object:
- {
- if (keep and (not callback
- or ((keep = callback(depth++, parse_event_t::object_start, result)) != 0)))
- {
- // explicitly set result to object to cope with {}
- result.m_type = value_t::object;
- result.m_value = value_t::object;
- }
-
- // read next token
- get_token();
-
- // closing } -> we are done
- if (last_token == lexer::token_type::end_object)
- {
- get_token();
- if (keep and callback and not callback(--depth, parse_event_t::object_end, result))
- {
- result = basic_json(value_t::discarded);
- }
- return result;
- }
-
- // no comma is expected here
- unexpect(lexer::token_type::value_separator);
-
- // otherwise: parse key-value pairs
- do
- {
- // ugly, but could be fixed with loop reorganization
- if (last_token == lexer::token_type::value_separator)
- {
- get_token();
- }
-
- // store key
- expect(lexer::token_type::value_string);
- const auto key = m_lexer.get_string();
-
- bool keep_tag = false;
- if (keep)
- {
- if (callback)
- {
- basic_json k(key);
- keep_tag = callback(depth, parse_event_t::key, k);
- }
- else
- {
- keep_tag = true;
- }
- }
-
- // parse separator (:)
- get_token();
- expect(lexer::token_type::name_separator);
-
- // parse and add value
- get_token();
- auto value = parse_internal(keep);
- if (keep and keep_tag and not value.is_discarded())
- {
- result[key] = std::move(value);
- }
- }
- while (last_token == lexer::token_type::value_separator);
-
- // closing }
- expect(lexer::token_type::end_object);
- get_token();
- if (keep and callback and not callback(--depth, parse_event_t::object_end, result))
- {
- result = basic_json(value_t::discarded);
- }
-
- return result;
- }
-
- case lexer::token_type::begin_array:
- {
- if (keep and (not callback
- or ((keep = callback(depth++, parse_event_t::array_start, result)) != 0)))
- {
- // explicitly set result to object to cope with []
- result.m_type = value_t::array;
- result.m_value = value_t::array;
- }
-
- // read next token
- get_token();
-
- // closing ] -> we are done
- if (last_token == lexer::token_type::end_array)
- {
- get_token();
- if (callback and not callback(--depth, parse_event_t::array_end, result))
- {
- result = basic_json(value_t::discarded);
- }
- return result;
- }
-
- // no comma is expected here
- unexpect(lexer::token_type::value_separator);
-
- // otherwise: parse values
- do
- {
- // ugly, but could be fixed with loop reorganization
- if (last_token == lexer::token_type::value_separator)
- {
- get_token();
- }
-
- // parse value
- auto value = parse_internal(keep);
- if (keep and not value.is_discarded())
- {
- result.push_back(std::move(value));
- }
- }
- while (last_token == lexer::token_type::value_separator);
-
- // closing ]
- expect(lexer::token_type::end_array);
- get_token();
- if (keep and callback and not callback(--depth, parse_event_t::array_end, result))
- {
- result = basic_json(value_t::discarded);
- }
-
- return result;
- }
-
- case lexer::token_type::literal_null:
- {
- get_token();
- result.m_type = value_t::null;
- break;
- }
-
- case lexer::token_type::value_string:
- {
- const auto s = m_lexer.get_string();
- get_token();
- result = basic_json(s);
- break;
- }
-
- case lexer::token_type::literal_true:
- {
- get_token();
- result.m_type = value_t::boolean;
- result.m_value = true;
- break;
- }
-
- case lexer::token_type::literal_false:
- {
- get_token();
- result.m_type = value_t::boolean;
- result.m_value = false;
- break;
- }
-
- case lexer::token_type::value_number:
- {
- m_lexer.get_number(result);
- get_token();
- break;
- }
-
- default:
- {
- // the last token was unexpected
- unexpect(last_token);
- }
- }
-
- if (keep and callback and not callback(depth, parse_event_t::value, result))
- {
- result = basic_json(value_t::discarded);
- }
- return result;
- }
-
- /// get next token from lexer
- typename lexer::token_type get_token()
- {
- last_token = m_lexer.scan();
- return last_token;
- }
-
- void expect(typename lexer::token_type t) const
- {
- if (t != last_token)
- {
- std::string error_msg = "parse error - unexpected ";
- error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() +
- "'") :
- lexer::token_type_name(last_token));
- error_msg += "; expected " + lexer::token_type_name(t);
- throw std::invalid_argument(error_msg);
- }
- }
-
- void unexpect(typename lexer::token_type t) const
- {
- if (t == last_token)
- {
- std::string error_msg = "parse error - unexpected ";
- error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() +
- "'") :
- lexer::token_type_name(last_token));
- throw std::invalid_argument(error_msg);
- }
- }
-
- private:
- /// current level of recursion
- int depth = 0;
- /// callback function
- const parser_callback_t callback = nullptr;
- /// the type of the last read token
- typename lexer::token_type last_token = lexer::token_type::uninitialized;
- /// the lexer
- lexer m_lexer;
- };
-
- public:
- /*!
- @brief JSON Pointer
-
- A JSON pointer defines a string syntax for identifying a specific value
- within a JSON document. It can be used with functions `at` and
- `operator[]`. Furthermore, JSON pointers are the base for JSON patches.
-
- @sa [RFC 6901](https://tools.ietf.org/html/rfc6901)
-
- @since version 2.0.0
- */
- class json_pointer
- {
- /// allow basic_json to access private members
- friend class basic_json;
-
- public:
- /*!
- @brief create JSON pointer
-
- Create a JSON pointer according to the syntax described in
- [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3).
-
- @param[in] s string representing the JSON pointer; if omitted, the
- empty string is assumed which references the whole JSON
- value
-
- @throw std::domain_error if reference token is nonempty and does not
- begin with a slash (`/`); example: `"JSON pointer must be empty or
- begin with /"`
- @throw std::domain_error if a tilde (`~`) is not followed by `0`
- (representing `~`) or `1` (representing `/`); example: `"escape error:
- ~ must be followed with 0 or 1"`
-
- @liveexample{The example shows the construction several valid JSON
- pointers as well as the exceptional behavior.,json_pointer}
-
- @since version 2.0.0
- */
- explicit json_pointer(const std::string& s = "")
- : reference_tokens(split(s))
- {}
-
- /*!
- @brief return a string representation of the JSON pointer
-
- @invariant For each JSON pointer `ptr`, it holds:
- @code {.cpp}
- ptr == json_pointer(ptr.to_string());
- @endcode
-
- @return a string representation of the JSON pointer
-
- @liveexample{The example shows the result of `to_string`.,
- json_pointer__to_string}
-
- @since version 2.0.0
- */
- std::string to_string() const noexcept
- {
- return std::accumulate(reference_tokens.begin(),
- reference_tokens.end(), std::string{},
- [](const std::string & a, const std::string & b)
- {
- return a + "/" + escape(b);
- });
- }
-
- /// @copydoc to_string()
- operator std::string() const
- {
- return to_string();
- }
-
- private:
- /// remove and return last reference pointer
- std::string pop_back()
- {
- if (is_root())
- {
- throw std::domain_error("JSON pointer has no parent");
- }
-
- auto last = reference_tokens.back();
- reference_tokens.pop_back();
- return last;
- }
-
- /// return whether pointer points to the root document
- bool is_root() const
- {
- return reference_tokens.empty();
- }
-
- json_pointer top() const
- {
- if (is_root())
- {
- throw std::domain_error("JSON pointer has no parent");
- }
-
- json_pointer result = *this;
- result.reference_tokens = {reference_tokens[0]};
- return result;
- }
-
- /*!
- @brief create and return a reference to the pointed to value
-
- @complexity Linear in the number of reference tokens.
- */
- reference get_and_create(reference j) const
- {
- pointer result = &j;
-
- // in case no reference tokens exist, return a reference to the
- // JSON value j which will be overwritten by a primitive value
- for (const auto& reference_token : reference_tokens)
- {
- switch (result->m_type)
- {
- case value_t::null:
- {
- if (reference_token == "0")
- {
- // start a new array if reference token is 0
- result = &result->operator[](0);
- }
- else
- {
- // start a new object otherwise
- result = &result->operator[](reference_token);
- }
- break;
- }
-
- case value_t::object:
- {
- // create an entry in the object
- result = &result->operator[](reference_token);
- break;
- }
-
- case value_t::array:
- {
- // create an entry in the array
- result = &result->operator[](static_cast<size_type>(std::stoi(reference_token)));
- break;
- }
-
- /*
- The following code is only reached if there exists a
- reference token _and_ the current value is primitive. In
- this case, we have an error situation, because primitive
- values may only occur as single value; that is, with an
- empty list of reference tokens.
- */
- default:
- {
- throw std::domain_error("invalid value to unflatten");
- }
- }
- }
-
- return *result;
- }
-
- /*!
- @brief return a reference to the pointed to value
-
- @note This version does not throw if a value is not present, but tries
- to create nested values instead. For instance, calling this function
- with pointer `"/this/that"` on a null value is equivalent to calling
- `operator[]("this").operator[]("that")` on that value, effectively
- changing the null value to an object.
-
- @param[in] ptr a JSON value
-
- @return reference to the JSON value pointed to by the JSON pointer
-
- @complexity Linear in the length of the JSON pointer.
-
- @throw std::out_of_range if the JSON pointer can not be resolved
- @throw std::domain_error if an array index begins with '0'
- @throw std::invalid_argument if an array index was not a number
- */
- reference get_unchecked(pointer ptr) const
- {
- for (const auto& reference_token : reference_tokens)
- {
- // convert null values to arrays or objects before continuing
- if (ptr->m_type == value_t::null)
- {
- // check if reference token is a number
- const bool nums = std::all_of(reference_token.begin(),
- reference_token.end(),
- [](const char x)
- {
- return std::isdigit(x);
- });
-
- // change value to array for numbers or "-" or to object
- // otherwise
- if (nums or reference_token == "-")
- {
- *ptr = value_t::array;
- }
- else
- {
- *ptr = value_t::object;
- }
- }
-
- switch (ptr->m_type)
- {
- case value_t::object:
- {
- // use unchecked object access
- ptr = &ptr->operator[](reference_token);
- break;
- }
-
- case value_t::array:
- {
- // error condition (cf. RFC 6901, Sect. 4)
- if (reference_token.size() > 1 and reference_token[0] == '0')
- {
- throw std::domain_error("array index must not begin with '0'");
- }
-
- if (reference_token == "-")
- {
- // explicityly treat "-" as index beyond the end
- ptr = &ptr->operator[](ptr->m_value.array->size());
- }
- else
- {
- // convert array index to number; unchecked access
- ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
- }
- break;
- }
-
- default:
- {
- throw std::out_of_range("unresolved reference token '" + reference_token + "'");
- }
- }
- }
-
- return *ptr;
- }
-
- reference get_checked(pointer ptr) const
- {
- for (const auto& reference_token : reference_tokens)
- {
- switch (ptr->m_type)
- {
- case value_t::object:
- {
- // note: at performs range check
- ptr = &ptr->at(reference_token);
- break;
- }
-
- case value_t::array:
- {
- if (reference_token == "-")
- {
- // "-" always fails the range check
- throw std::out_of_range("array index '-' (" +
- std::to_string(ptr->m_value.array->size()) +
- ") is out of range");
- }
-
- // error condition (cf. RFC 6901, Sect. 4)
- if (reference_token.size() > 1 and reference_token[0] == '0')
- {
- throw std::domain_error("array index must not begin with '0'");
- }
-
- // note: at performs range check
- ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token)));
- break;
- }
-
- default:
- {
- throw std::out_of_range("unresolved reference token '" + reference_token + "'");
- }
- }
- }
-
- return *ptr;
- }
-
- /*!
- @brief return a const reference to the pointed to value
-
- @param[in] ptr a JSON value
-
- @return const reference to the JSON value pointed to by the JSON
- pointer
- */
- const_reference get_unchecked(const_pointer ptr) const
- {
- for (const auto& reference_token : reference_tokens)
- {
- switch (ptr->m_type)
- {
- case value_t::object:
- {
- // use unchecked object access
- ptr = &ptr->operator[](reference_token);
- break;
- }
-
- case value_t::array:
- {
- if (reference_token == "-")
- {
- // "-" cannot be used for const access
- throw std::out_of_range("array index '-' (" +
- std::to_string(ptr->m_value.array->size()) +
- ") is out of range");
- }
-
- // error condition (cf. RFC 6901, Sect. 4)
- if (reference_token.size() > 1 and reference_token[0] == '0')
- {
- throw std::domain_error("array index must not begin with '0'");
- }
-
- // use unchecked array access
- ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
- break;
- }
-
- default:
- {
- throw std::out_of_range("unresolved reference token '" + reference_token + "'");
- }
- }
- }
-
- return *ptr;
- }
-
- const_reference get_checked(const_pointer ptr) const
- {
- for (const auto& reference_token : reference_tokens)
- {
- switch (ptr->m_type)
- {
- case value_t::object:
- {
- // note: at performs range check
- ptr = &ptr->at(reference_token);
- break;
- }
-
- case value_t::array:
- {
- if (reference_token == "-")
- {
- // "-" always fails the range check
- throw std::out_of_range("array index '-' (" +
- std::to_string(ptr->m_value.array->size()) +
- ") is out of range");
- }
-
- // error condition (cf. RFC 6901, Sect. 4)
- if (reference_token.size() > 1 and reference_token[0] == '0')
- {
- throw std::domain_error("array index must not begin with '0'");
- }
-
- // note: at performs range check
- ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token)));
- break;
- }
-
- default:
- {
- throw std::out_of_range("unresolved reference token '" + reference_token + "'");
- }
- }
- }
-
- return *ptr;
- }
-
- /// split the string input to reference tokens
- static std::vector<std::string> split(const std::string& reference_string)
- {
- std::vector<std::string> result;
-
- // special case: empty reference string -> no reference tokens
- if (reference_string.empty())
- {
- return result;
- }
-
- // check if nonempty reference string begins with slash
- if (reference_string[0] != '/')
- {
- throw std::domain_error("JSON pointer must be empty or begin with '/'");
- }
-
- // extract the reference tokens:
- // - slash: position of the last read slash (or end of string)
- // - start: position after the previous slash
- for (
- // search for the first slash after the first character
- size_t slash = reference_string.find_first_of("/", 1),
- // set the beginning of the first reference token
- start = 1;
- // we can stop if start == string::npos+1 = 0
- start != 0;
- // set the beginning of the next reference token
- // (will eventually be 0 if slash == std::string::npos)
- start = slash + 1,
- // find next slash
- slash = reference_string.find_first_of("/", start))
- {
- // use the text between the beginning of the reference token
- // (start) and the last slash (slash).
- auto reference_token = reference_string.substr(start, slash - start);
-
- // check reference tokens are properly escaped
- for (size_t pos = reference_token.find_first_of("~");
- pos != std::string::npos;
- pos = reference_token.find_first_of("~", pos + 1))
- {
- assert(reference_token[pos] == '~');
-
- // ~ must be followed by 0 or 1
- if (pos == reference_token.size() - 1 or
- (reference_token[pos + 1] != '0' and
- reference_token[pos + 1] != '1'))
- {
- throw std::domain_error("escape error: '~' must be followed with '0' or '1'");
- }
- }
-
- // finally, store the reference token
- unescape(reference_token);
- result.push_back(reference_token);
- }
-
- return result;
- }
-
- private:
- /*!
- @brief replace all occurrences of a substring by another string
-
- @param[in,out] s the string to manipulate; changed so that all
- occurrences of @a f are replaced with @a t
- @param[in] f the substring to replace with @a t
- @param[in] t the string to replace @a f
-
- @pre The search string @a f must not be empty.
-
- @since version 2.0.0
- */
- static void replace_substring(std::string& s,
- const std::string& f,
- const std::string& t)
- {
- assert(not f.empty());
-
- for (
- size_t pos = s.find(f); // find first occurrence of f
- pos != std::string::npos; // make sure f was found
- s.replace(pos, f.size(), t), // replace with t
- pos = s.find(f, pos + t.size()) // find next occurrence of f
- );
- }
-
- /// escape tilde and slash
- static std::string escape(std::string s)
- {
- // escape "~"" to "~0" and "/" to "~1"
- replace_substring(s, "~", "~0");
- replace_substring(s, "/", "~1");
- return s;
- }
-
- /// unescape tilde and slash
- static void unescape(std::string& s)
- {
- // first transform any occurrence of the sequence '~1' to '/'
- replace_substring(s, "~1", "/");
- // then transform any occurrence of the sequence '~0' to '~'
- replace_substring(s, "~0", "~");
- }
-
- /*!
- @param[in] reference_string the reference string to the current value
- @param[in] value the value to consider
- @param[in,out] result the result object to insert values to
-
- @note Empty objects or arrays are flattened to `null`.
- */
- static void flatten(const std::string& reference_string,
- const basic_json& value,
- basic_json& result)
- {
- switch (value.m_type)
- {
- case value_t::array:
- {
- if (value.m_value.array->empty())
- {
- // flatten empty array as null
- result[reference_string] = nullptr;
- }
- else
- {
- // iterate array and use index as reference string
- for (size_t i = 0; i < value.m_value.array->size(); ++i)
- {
- flatten(reference_string + "/" + std::to_string(i),
- value.m_value.array->operator[](i), result);
- }
- }
- break;
- }
-
- case value_t::object:
- {
- if (value.m_value.object->empty())
- {
- // flatten empty object as null
- result[reference_string] = nullptr;
- }
- else
- {
- // iterate object and use keys as reference string
- for (const auto& element : *value.m_value.object)
- {
- flatten(reference_string + "/" + escape(element.first),
- element.second, result);
- }
- }
- break;
- }
-
- default:
- {
- // add primitive value with its reference string
- result[reference_string] = value;
- break;
- }
- }
- }
-
- /*!
- @param[in] value flattened JSON
-
- @return unflattened JSON
- */
- static basic_json unflatten(const basic_json& value)
- {
- if (not value.is_object())
- {
- throw std::domain_error("only objects can be unflattened");
- }
-
- basic_json result;
-
- // iterate the JSON object values
- for (const auto& element : *value.m_value.object)
- {
- if (not element.second.is_primitive())
- {
- throw std::domain_error("values in object must be primitive");
- }
-
- // assign value to reference pointed to by JSON pointer; Note
- // that if the JSON pointer is "" (i.e., points to the whole
- // value), function get_and_create returns a reference to
- // result itself. An assignment will then create a primitive
- // value.
- json_pointer(element.first).get_and_create(result) = element.second;
- }
-
- return result;
- }
+ return binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).parse_msgpack(strict);
+ }
- private:
- /// the reference tokens
- std::vector<std::string> reference_tokens {};
- };
+ /// @}
//////////////////////////
// JSON Pointer support //
@@ -11550,9 +13677,9 @@ basic_json_parser_66:
@complexity Constant.
- @throw std::out_of_range if the JSON pointer can not be resolved
- @throw std::domain_error if an array index begins with '0'
- @throw std::invalid_argument if an array index was not a number
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index was not a number
+ @throw out_of_range.404 if the JSON pointer can not be resolved
@liveexample{The behavior is shown in the example.,operatorjson_pointer}
@@ -11577,9 +13704,10 @@ basic_json_parser_66:
@complexity Constant.
- @throw std::out_of_range if the JSON pointer can not be resolved
- @throw std::domain_error if an array index begins with '0'
- @throw std::invalid_argument if an array index was not a number
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index was not a number
+ @throw out_of_range.402 if the array index '-' is used
+ @throw out_of_range.404 if the JSON pointer can not be resolved
@liveexample{The behavior is shown in the example.,operatorjson_pointer_const}
@@ -11600,15 +13728,33 @@ basic_json_parser_66:
@return reference to the element pointed to by @a ptr
- @complexity Constant.
+ @throw parse_error.106 if an array index in the passed JSON pointer @a ptr
+ begins with '0'. See example below.
- @throw std::out_of_range if the JSON pointer can not be resolved
- @throw std::domain_error if an array index begins with '0'
- @throw std::invalid_argument if an array index was not a number
+ @throw parse_error.109 if an array index in the passed JSON pointer @a ptr
+ is not a number. See example below.
- @liveexample{The behavior is shown in the example.,at_json_pointer}
+ @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr
+ is out of range. See example below.
+
+ @throw out_of_range.402 if the array index '-' is used in the passed JSON
+ pointer @a ptr. As `at` provides checked access (and no elements are
+ implicitly inserted), the index '-' is always invalid. See example below.
+
+ @throw out_of_range.403 if the JSON pointer describes a key of an object
+ which cannot be found. See example below.
+
+ @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved.
+ See example below.
+
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes in the JSON value.
+
+ @complexity Constant.
@since version 2.0.0
+
+ @liveexample{The behavior is shown in the example.,at_json_pointer}
*/
reference at(const json_pointer& ptr)
{
@@ -11625,15 +13771,33 @@ basic_json_parser_66:
@return reference to the element pointed to by @a ptr
- @complexity Constant.
+ @throw parse_error.106 if an array index in the passed JSON pointer @a ptr
+ begins with '0'. See example below.
- @throw std::out_of_range if the JSON pointer can not be resolved
- @throw std::domain_error if an array index begins with '0'
- @throw std::invalid_argument if an array index was not a number
+ @throw parse_error.109 if an array index in the passed JSON pointer @a ptr
+ is not a number. See example below.
- @liveexample{The behavior is shown in the example.,at_json_pointer_const}
+ @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr
+ is out of range. See example below.
+
+ @throw out_of_range.402 if the array index '-' is used in the passed JSON
+ pointer @a ptr. As `at` provides checked access (and no elements are
+ implicitly inserted), the index '-' is always invalid. See example below.
+
+ @throw out_of_range.403 if the JSON pointer describes a key of an object
+ which cannot be found. See example below.
+
+ @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved.
+ See example below.
+
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes in the JSON value.
+
+ @complexity Constant.
@since version 2.0.0
+
+ @liveexample{The behavior is shown in the example.,at_json_pointer_const}
*/
const_reference at(const json_pointer& ptr) const
{
@@ -11648,7 +13812,7 @@ basic_json_parser_66:
primitive. The original JSON value can be restored using the @ref
unflatten() function.
- @return an object that maps JSON pointers to primitve values
+ @return an object that maps JSON pointers to primitive values
@note Empty objects and arrays are flattened to `null` and will not be
reconstructed correctly by the @ref unflatten() function.
@@ -11689,6 +13853,9 @@ basic_json_parser_66:
@complexity Linear in the size the JSON value.
+ @throw type_error.314 if value is not an object
+ @throw type_error.315 if object values are not primitive
+
@liveexample{The following code shows how a flattened JSON object is
unflattened into the original nested JSON object.,unflatten}
@@ -11715,7 +13882,7 @@ basic_json_parser_66:
[JSON Patch](http://jsonpatch.com) defines a JSON document structure for
expressing a sequence of operations to apply to a JSON) document. With
- this funcion, a JSON Patch is applied to the current JSON value by
+ this function, a JSON Patch is applied to the current JSON value by
executing all operations from the patch.
@param[in] json_patch JSON patch document
@@ -11726,12 +13893,23 @@ basic_json_parser_66:
any case, the original value is not changed: the patch is applied
to a copy of the value.
- @throw std::out_of_range if a JSON pointer inside the patch could not
- be resolved successfully in the current JSON value; example: `"key baz
- not found"`
- @throw invalid_argument if the JSON patch is malformed (e.g., mandatory
+ @throw parse_error.104 if the JSON patch does not consist of an array of
+ objects
+
+ @throw parse_error.105 if the JSON patch is malformed (e.g., mandatory
attributes are missing); example: `"operation add must have member path"`
+ @throw out_of_range.401 if an array index is out of range.
+
+ @throw out_of_range.403 if a JSON pointer inside the patch could not be
+ resolved successfully in the current JSON value; example: `"key baz not
+ found"`
+
+ @throw out_of_range.405 if JSON pointer has no parent ("add", "remove",
+ "move")
+
+ @throw other_error.501 if "test" operation was unsuccessful
+
@complexity Linear in the size of the JSON value and the length of the
JSON patch. As usually only a fraction of the JSON value is affected by
the patch, the complexity can usually be neglected.
@@ -11754,7 +13932,7 @@ basic_json_parser_66:
// the valid JSON Patch operations
enum class patch_operations {add, remove, replace, move, copy, test, invalid};
- const auto get_op = [](const std::string op)
+ const auto get_op = [](const std::string & op)
{
if (op == "add")
{
@@ -11824,11 +14002,11 @@ basic_json_parser_66:
}
else
{
- const auto idx = std::stoi(last_path);
- if (static_cast<size_type>(idx) > parent.size())
+ const auto idx = json_pointer::array_index(last_path);
+ if (JSON_UNLIKELY(static_cast<size_type>(idx) > parent.size()))
{
// avoid undefined behavior
- throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+ JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
}
else
{
@@ -11860,30 +14038,29 @@ basic_json_parser_66:
{
// perform range check
auto it = parent.find(last_path);
- if (it != parent.end())
+ if (JSON_LIKELY(it != parent.end()))
{
parent.erase(it);
}
else
{
- throw std::out_of_range("key '" + last_path + "' not found");
+ JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found"));
}
}
else if (parent.is_array())
{
// note erase performs range check
- parent.erase(static_cast<size_type>(std::stoi(last_path)));
+ parent.erase(static_cast<size_type>(json_pointer::array_index(last_path)));
}
};
- // type check
- if (not json_patch.is_array())
+ // type check: top level value must be an array
+ if (JSON_UNLIKELY(not json_patch.is_array()))
{
- // a JSON patch must be an array of objects
- throw std::invalid_argument("JSON patch must be an array of objects");
+ JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects"));
}
- // iterate and apply th eoperations
+ // iterate and apply the operations
for (const auto& val : json_patch)
{
// wrapper to get a value for an operation
@@ -11898,25 +14075,25 @@ basic_json_parser_66:
const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'";
// check if desired value is present
- if (it == val.m_value.object->end())
+ if (JSON_UNLIKELY(it == val.m_value.object->end()))
{
- throw std::invalid_argument(error_msg + " must have member '" + member + "'");
+ JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'"));
}
// check if result is of type string
- if (string_type and not it->second.is_string())
+ if (JSON_UNLIKELY(string_type and not it->second.is_string()))
{
- throw std::invalid_argument(error_msg + " must have string member '" + member + "'");
+ JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'"));
}
// no error: return value
return it->second;
};
- // type check
- if (not val.is_object())
+ // type check: every element of the array must be an object
+ if (JSON_UNLIKELY(not val.is_object()))
{
- throw std::invalid_argument("JSON patch must be an array of objects");
+ JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects"));
}
// collect mandatory members
@@ -11964,32 +14141,37 @@ basic_json_parser_66:
case patch_operations::copy:
{
- const std::string from_path = get_value("copy", "from", true);;
+ const std::string from_path = get_value("copy", "from", true);
const json_pointer from_ptr(from_path);
// the "from" location must exist - use at()
- result[ptr] = result.at(from_ptr);
+ basic_json v = result.at(from_ptr);
+
+ // The copy is functionally identical to an "add"
+ // operation at the target location using the value
+ // specified in the "from" member.
+ operation_add(ptr, v);
break;
}
case patch_operations::test:
{
bool success = false;
- try
+ JSON_TRY
{
// check if "value" matches the one at "path"
// the "path" location must exist - use at()
success = (result.at(ptr) == get_value("test", "value", false));
}
- catch (std::out_of_range&)
+ JSON_CATCH (out_of_range&)
{
// ignore out of range errors: success remains false
}
// throw an exception if test fails
- if (not success)
+ if (JSON_UNLIKELY(not success))
{
- throw std::domain_error("unsuccessful: " + val.dump());
+ JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump()));
}
break;
@@ -11999,7 +14181,7 @@ basic_json_parser_66:
{
// op must be "add", "remove", "replace", "move", "copy", or
// "test"
- throw std::invalid_argument("operation value '" + op + "' is invalid");
+ JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid"));
}
}
}
@@ -12022,8 +14204,8 @@ basic_json_parser_66:
@note Currently, only `remove`, `add`, and `replace` operations are
generated.
- @param[in] source JSON value to copare from
- @param[in] target JSON value to copare against
+ @param[in] source JSON value to compare from
+ @param[in] target JSON value to compare against
@param[in] path helper value to create JSON pointers
@return a JSON patch to convert the @a source to @a target
@@ -12039,8 +14221,7 @@ basic_json_parser_66:
@since version 2.0.0
*/
- static basic_json diff(const basic_json& source,
- const basic_json& target,
+ static basic_json diff(const basic_json& source, const basic_json& target,
const std::string& path = "")
{
// the patch
@@ -12057,9 +14238,7 @@ basic_json_parser_66:
// different types: replace value
result.push_back(
{
- {"op", "replace"},
- {"path", path},
- {"value", target}
+ {"op", "replace"}, {"path", path}, {"value", target}
});
}
else
@@ -12069,7 +14248,7 @@ basic_json_parser_66:
case value_t::array:
{
// first pass: traverse common elements
- size_t i = 0;
+ std::size_t i = 0;
while (i < source.size() and i < target.size())
{
// recursive call to compare array values at index i
@@ -12113,7 +14292,7 @@ basic_json_parser_66:
case value_t::object:
{
// first pass: traverse this object's elements
- for (auto it = source.begin(); it != source.end(); ++it)
+ for (auto it = source.cbegin(); it != source.cend(); ++it)
{
// escape the key name to be used in a JSON patch
const auto key = json_pointer::escape(it.key());
@@ -12129,14 +14308,13 @@ basic_json_parser_66:
// found a key that is not in o -> remove it
result.push_back(object(
{
- {"op", "remove"},
- {"path", path + "/" + key}
+ {"op", "remove"}, {"path", path + "/" + key}
}));
}
}
// second pass: traverse other object's elements
- for (auto it = target.begin(); it != target.end(); ++it)
+ for (auto it = target.cbegin(); it != target.cend(); ++it)
{
if (source.find(it.key()) == source.end())
{
@@ -12144,8 +14322,7 @@ basic_json_parser_66:
const auto key = json_pointer::escape(it.key());
result.push_back(
{
- {"op", "add"},
- {"path", path + "/" + key},
+ {"op", "add"}, {"path", path + "/" + key},
{"value", it.value()}
});
}
@@ -12159,9 +14336,7 @@ basic_json_parser_66:
// both primitive type: replace value
result.push_back(
{
- {"op", "replace"},
- {"path", path},
- {"value", target}
+ {"op", "replace"}, {"path", path}, {"value", target}
});
break;
}
@@ -12174,7 +14349,6 @@ basic_json_parser_66:
/// @}
};
-
/////////////
// presets //
/////////////
@@ -12188,8 +14362,402 @@ uses the standard template types.
@since version 1.0.0
*/
using json = basic_json<>;
+
+//////////////////
+// json_pointer //
+//////////////////
+
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+NLOHMANN_BASIC_JSON_TPL&
+json_pointer::get_and_create(NLOHMANN_BASIC_JSON_TPL& j) const
+{
+ using size_type = typename NLOHMANN_BASIC_JSON_TPL::size_type;
+ auto result = &j;
+
+ // in case no reference tokens exist, return a reference to the JSON value
+ // j which will be overwritten by a primitive value
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (result->m_type)
+ {
+ case detail::value_t::null:
+ {
+ if (reference_token == "0")
+ {
+ // start a new array if reference token is 0
+ result = &result->operator[](0);
+ }
+ else
+ {
+ // start a new object otherwise
+ result = &result->operator[](reference_token);
+ }
+ break;
+ }
+
+ case detail::value_t::object:
+ {
+ // create an entry in the object
+ result = &result->operator[](reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ // create an entry in the array
+ JSON_TRY
+ {
+ result = &result->operator[](static_cast<size_type>(array_index(reference_token)));
+ }
+ JSON_CATCH(std::invalid_argument&)
+ {
+ JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
+ }
+ break;
+ }
+
+ /*
+ The following code is only reached if there exists a reference
+ token _and_ the current value is primitive. In this case, we have
+ an error situation, because primitive values may only occur as
+ single value; that is, with an empty list of reference tokens.
+ */
+ default:
+ JSON_THROW(detail::type_error::create(313, "invalid value to unflatten"));
+ }
+ }
+
+ return *result;
}
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+NLOHMANN_BASIC_JSON_TPL&
+json_pointer::get_unchecked(NLOHMANN_BASIC_JSON_TPL* ptr) const
+{
+ using size_type = typename NLOHMANN_BASIC_JSON_TPL::size_type;
+ for (const auto& reference_token : reference_tokens)
+ {
+ // convert null values to arrays or objects before continuing
+ if (ptr->m_type == detail::value_t::null)
+ {
+ // check if reference token is a number
+ const bool nums =
+ std::all_of(reference_token.begin(), reference_token.end(),
+ [](const char x)
+ {
+ return (x >= '0' and x <= '9');
+ });
+
+ // change value to array for numbers or "-" or to object otherwise
+ *ptr = (nums or reference_token == "-")
+ ? detail::value_t::array
+ : detail::value_t::object;
+ }
+
+ switch (ptr->m_type)
+ {
+ case detail::value_t::object:
+ {
+ // use unchecked object access
+ ptr = &ptr->operator[](reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ // error condition (cf. RFC 6901, Sect. 4)
+ if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
+ {
+ JSON_THROW(detail::parse_error::create(106, 0,
+ "array index '" + reference_token +
+ "' must not begin with '0'"));
+ }
+
+ if (reference_token == "-")
+ {
+ // explicitly treat "-" as index beyond the end
+ ptr = &ptr->operator[](ptr->m_value.array->size());
+ }
+ else
+ {
+ // convert array index to number; unchecked access
+ JSON_TRY
+ {
+ ptr = &ptr->operator[](
+ static_cast<size_type>(array_index(reference_token)));
+ }
+ JSON_CATCH(std::invalid_argument&)
+ {
+ JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
+ }
+ }
+ break;
+ }
+
+ default:
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
+ }
+ }
+
+ return *ptr;
+}
+
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+NLOHMANN_BASIC_JSON_TPL&
+json_pointer::get_checked(NLOHMANN_BASIC_JSON_TPL* ptr) const
+{
+ using size_type = typename NLOHMANN_BASIC_JSON_TPL::size_type;
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (ptr->m_type)
+ {
+ case detail::value_t::object:
+ {
+ // note: at performs range check
+ ptr = &ptr->at(reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ if (JSON_UNLIKELY(reference_token == "-"))
+ {
+ // "-" always fails the range check
+ JSON_THROW(detail::out_of_range::create(402,
+ "array index '-' (" + std::to_string(ptr->m_value.array->size()) +
+ ") is out of range"));
+ }
+
+ // error condition (cf. RFC 6901, Sect. 4)
+ if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
+ {
+ JSON_THROW(detail::parse_error::create(106, 0,
+ "array index '" + reference_token +
+ "' must not begin with '0'"));
+ }
+
+ // note: at performs range check
+ JSON_TRY
+ {
+ ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
+ }
+ JSON_CATCH(std::invalid_argument&)
+ {
+ JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
+ }
+ break;
+ }
+
+ default:
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
+ }
+ }
+
+ return *ptr;
+}
+
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+const NLOHMANN_BASIC_JSON_TPL&
+json_pointer::get_unchecked(const NLOHMANN_BASIC_JSON_TPL* ptr) const
+{
+ using size_type = typename NLOHMANN_BASIC_JSON_TPL::size_type;
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (ptr->m_type)
+ {
+ case detail::value_t::object:
+ {
+ // use unchecked object access
+ ptr = &ptr->operator[](reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ if (JSON_UNLIKELY(reference_token == "-"))
+ {
+ // "-" cannot be used for const access
+ JSON_THROW(detail::out_of_range::create(402,
+ "array index '-' (" + std::to_string(ptr->m_value.array->size()) +
+ ") is out of range"));
+ }
+
+ // error condition (cf. RFC 6901, Sect. 4)
+ if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
+ {
+ JSON_THROW(detail::parse_error::create(106, 0,
+ "array index '" + reference_token +
+ "' must not begin with '0'"));
+ }
+
+ // use unchecked array access
+ JSON_TRY
+ {
+ ptr = &ptr->operator[](
+ static_cast<size_type>(array_index(reference_token)));
+ }
+ JSON_CATCH(std::invalid_argument&)
+ {
+ JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
+ }
+ break;
+ }
+
+ default:
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
+ }
+ }
+
+ return *ptr;
+}
+
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+const NLOHMANN_BASIC_JSON_TPL&
+json_pointer::get_checked(const NLOHMANN_BASIC_JSON_TPL* ptr) const
+{
+ using size_type = typename NLOHMANN_BASIC_JSON_TPL::size_type;
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (ptr->m_type)
+ {
+ case detail::value_t::object:
+ {
+ // note: at performs range check
+ ptr = &ptr->at(reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ if (JSON_UNLIKELY(reference_token == "-"))
+ {
+ // "-" always fails the range check
+ JSON_THROW(detail::out_of_range::create(402,
+ "array index '-' (" + std::to_string(ptr->m_value.array->size()) +
+ ") is out of range"));
+ }
+
+ // error condition (cf. RFC 6901, Sect. 4)
+ if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
+ {
+ JSON_THROW(detail::parse_error::create(106, 0,
+ "array index '" + reference_token +
+ "' must not begin with '0'"));
+ }
+
+ // note: at performs range check
+ JSON_TRY
+ {
+ ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
+ }
+ JSON_CATCH(std::invalid_argument&)
+ {
+ JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
+ }
+ break;
+ }
+
+ default:
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
+ }
+ }
+
+ return *ptr;
+}
+
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+void json_pointer::flatten(const std::string& reference_string,
+ const NLOHMANN_BASIC_JSON_TPL& value,
+ NLOHMANN_BASIC_JSON_TPL& result)
+{
+ switch (value.m_type)
+ {
+ case detail::value_t::array:
+ {
+ if (value.m_value.array->empty())
+ {
+ // flatten empty array as null
+ result[reference_string] = nullptr;
+ }
+ else
+ {
+ // iterate array and use index as reference string
+ for (std::size_t i = 0; i < value.m_value.array->size(); ++i)
+ {
+ flatten(reference_string + "/" + std::to_string(i),
+ value.m_value.array->operator[](i), result);
+ }
+ }
+ break;
+ }
+
+ case detail::value_t::object:
+ {
+ if (value.m_value.object->empty())
+ {
+ // flatten empty object as null
+ result[reference_string] = nullptr;
+ }
+ else
+ {
+ // iterate object and use keys as reference string
+ for (const auto& element : *value.m_value.object)
+ {
+ flatten(reference_string + "/" + escape(element.first), element.second, result);
+ }
+ }
+ break;
+ }
+
+ default:
+ {
+ // add primitive value with its reference string
+ result[reference_string] = value;
+ break;
+ }
+ }
+}
+
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+NLOHMANN_BASIC_JSON_TPL
+json_pointer::unflatten(const NLOHMANN_BASIC_JSON_TPL& value)
+{
+ if (JSON_UNLIKELY(not value.is_object()))
+ {
+ JSON_THROW(detail::type_error::create(314, "only objects can be unflattened"));
+ }
+
+ NLOHMANN_BASIC_JSON_TPL result;
+
+ // iterate the JSON object values
+ for (const auto& element : *value.m_value.object)
+ {
+ if (JSON_UNLIKELY(not element.second.is_primitive()))
+ {
+ JSON_THROW(detail::type_error::create(315, "values in object must be primitive"));
+ }
+
+ // assign value to reference pointed to by JSON pointer; Note that if
+ // the JSON pointer is "" (i.e., points to the whole value), function
+ // get_and_create returns a reference to result itself. An assignment
+ // will then create a primitive value.
+ json_pointer(element.first).get_and_create(result) = element.second;
+ }
+
+ return result;
+}
+
+inline bool operator==(json_pointer const& lhs, json_pointer const& rhs) noexcept
+{
+ return (lhs.reference_tokens == rhs.reference_tokens);
+}
+
+inline bool operator!=(json_pointer const& lhs, json_pointer const& rhs) noexcept
+{
+ return not (lhs == rhs);
+}
+} // namespace nlohmann
+
///////////////////////
// nonmember support //
@@ -12229,7 +14797,25 @@ struct hash<nlohmann::json>
return h(j.dump());
}
};
-}
+
+/// specialization for std::less<value_t>
+/// @note: do not remove the space after '<',
+/// see https://github.com/nlohmann/json/pull/679
+template<>
+struct less< ::nlohmann::detail::value_t>
+{
+ /*!
+ @brief compare two value_t enum values
+ @since version 3.0.0
+ */
+ bool operator()(nlohmann::detail::value_t lhs,
+ nlohmann::detail::value_t rhs) const noexcept
+ {
+ return nlohmann::detail::operator<(lhs, rhs);
+ }
+};
+
+} // namespace std
/*!
@brief user-defined string literal for JSON values
@@ -12271,5 +14857,18 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic pop
#endif
+#if defined(__clang__)
+ #pragma GCC diagnostic pop
+#endif
+
+// clean up
+#undef JSON_CATCH
+#undef JSON_THROW
+#undef JSON_TRY
+#undef JSON_LIKELY
+#undef JSON_UNLIKELY
+#undef JSON_DEPRECATED
+#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION
+#undef NLOHMANN_BASIC_JSON_TPL
#endif
diff --git a/ext/librethinkdbxx/.travis.yml b/ext/librethinkdbxx/.travis.yml
new file mode 100644
index 00000000..b306a410
--- /dev/null
+++ b/ext/librethinkdbxx/.travis.yml
@@ -0,0 +1,11 @@
+sudo: required
+dist: trusty
+
+python:
+ - "3.4.3"
+
+addons:
+ rethinkdb: "2.3"
+
+script:
+ - make test
diff --git a/ext/librethinkdbxx/COPYRIGHT b/ext/librethinkdbxx/COPYRIGHT
new file mode 100644
index 00000000..c25145d5
--- /dev/null
+++ b/ext/librethinkdbxx/COPYRIGHT
@@ -0,0 +1,16 @@
+RethinkDB Language Drivers
+
+Copyright 2010-2012 RethinkDB
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this product except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
diff --git a/ext/librethinkdbxx/Makefile b/ext/librethinkdbxx/Makefile
new file mode 100644
index 00000000..ba319c16
--- /dev/null
+++ b/ext/librethinkdbxx/Makefile
@@ -0,0 +1,126 @@
+# Customisable build settings
+
+CXX ?= clang++
+CXXFLAGS ?=
+INCLUDE_PYTHON_DOCS ?= no
+DEBUG ?= no
+PYTHON ?= python3
+
+# Required build settings
+
+ifneq (no,$(DEBUG))
+ CXXFLAGS += -ggdb
+else
+ CXXFLAGS += -O3 # -flto
+endif
+
+CXXFLAGS += -std=c++11 -I'build/gen' -Wall -pthread -fPIC
+
+prefix ?= /usr
+DESTDIR ?=
+
+.DELETE_ON_ERROR:
+SHELL := /bin/bash
+
+modules := connection datum json term cursor types utils
+headers := utils error exceptions types datum connection cursor term
+
+o_files := $(patsubst %, build/obj/%.o, $(modules))
+d_files := $(patsubst %, build/dep/%.d, $(modules))
+
+skip_tests := regression/1133 regression/767 regression/1005 # python-only
+skip_tests += arity # arity errors are compile-time
+skip_tests += geo # geo types not implemented yet
+skip_tests += limits # possibly broken tests: https://github.com/rethinkdb/rethinkdb/issues/5940
+
+upstream_tests := \
+ $(filter-out %.rb.%, \
+ $(filter-out $(patsubst %,test/upstream/%%, $(skip_tests)), \
+ $(filter test/upstream/$(test_filter)%, \
+ $(shell find test/upstream -name '*.yaml' | egrep -v '.(rb|js).yaml$$'))))
+upstream_tests_cc := $(patsubst %.yaml, build/tests/%.cc, $(upstream_tests))
+upstream_tests_o := $(patsubst %.cc, %.o, $(upstream_tests_cc))
+
+.PRECIOUS: $(upstream_tests_cc) $(upstream_tests_o)
+
+default: build/librethinkdb++.a build/include/rethinkdb.h build/librethinkdb++.so
+
+all: default build/test
+
+build/librethinkdb++.a: $(o_files)
+ ar rcs $@ $^
+
+build/librethinkdb++.so: $(o_files)
+ $(CXX) -o $@ $(CXXFLAGS) -shared $^
+
+build/obj/%.o: src/%.cc build/gen/protocol_defs.h
+ @mkdir -p $(dir $@)
+ @mkdir -p $(dir build/dep/$*.d)
+ $(CXX) -o $@ $(CXXFLAGS) -c $< -MP -MQ $@ -MD -MF build/dep/$*.d
+
+build/gen/protocol_defs.h: reql/ql2.proto reql/gen.py | build/gen/.
+ $(PYTHON) reql/gen.py $< > $@
+
+clean:
+ rm -rf build
+
+ifneq (no,$(INCLUDE_PYTHON_DOCS))
+build/include/rethinkdb.h: build/rethinkdb.nodocs.h reql/add_docs.py reql/python_docs.txt | build/include/.
+ $(PYTHON) reql/add_docs.py reql/python_docs.txt < $< > $@
+else
+build/include/rethinkdb.h: build/rethinkdb.nodocs.h | build/include/.
+ cp $< $@
+endif
+
+build/rethinkdb.nodocs.h: build/gen/protocol_defs.h $(patsubst %, src/%.h, $(headers))
+ ( echo "// Auto-generated file, built from $^"; \
+ echo '#pragma once'; \
+ cat $^ | \
+ grep -v '^#pragma once' | \
+ grep -v '^#include "'; \
+ ) > $@
+
+build/tests/%.cc: %.yaml test/yaml_to_cxx.py
+ @mkdir -p $(dir $@)
+ $(PYTHON) test/yaml_to_cxx.py $< > $@
+
+build/tests/upstream_tests.cc: $(upstream_tests) test/gen_index_cxx.py FORCE | build/tests/.
+ @echo '$(PYTHON) test/gen_index_cxx.py $(wordlist 1,5,$(upstream_tests)) ... > $@'
+ @$(PYTHON) test/gen_index_cxx.py $(upstream_tests) > $@
+
+build/tests/%.o: build/tests/%.cc build/include/rethinkdb.h test/testlib.h | build/tests/.
+ $(CXX) -o $@ $(CXXFLAGS) -isystem build/include -I test -c $< -Wno-unused-variable
+
+build/tests/%.o: test/%.cc test/testlib.h build/include/rethinkdb.h | build/tests/.
+ $(CXX) -o $@ $(CXXFLAGS) -isystem build/include -I test -c $<
+
+build/test: build/tests/testlib.o build/tests/test.o build/tests/upstream_tests.o $(upstream_tests_o) build/librethinkdb++.a
+ @echo $(CXX) -o $@ $(CXXFLAGS) $(wordlist 1,5,$^) ...
+ @$(CXX) -o $@ $(CXXFLAGS) build/librethinkdb++.a $^
+
+.PHONY: test
+test: build/test
+ build/test
+
+build/bench: build/tests/bench.o build/librethinkdb++.a
+ @$(CXX) -o $@ $(CXXFLAGS) -isystem build/include build/librethinkdb++.a $^
+
+.PHONY: bench
+bench: build/bench
+ build/bench
+
+.PHONY: install
+install: build/librethinkdb++.a build/include/rethinkdb.h build/librethinkdb++.so
+ install -m755 -d $(DESTDIR)$(prefix)/lib
+ install -m755 -d $(DESTDIR)$(prefix)/include
+ install -m644 build/librethinkdb++.a $(DESTDIR)$(prefix)/lib/librethinkdb++.a
+ install -m644 build/librethinkdb++.so $(DESTDIR)$(prefix)/lib/librethinkdb++.so
+ install -m644 build/include/rethinkdb.h $(DESTDIR)$(prefix)/include/rethinkdb.h
+
+%/.:
+ mkdir -p $*
+
+.PHONY: FORCE
+FORCE:
+
+-include $(d_files)
diff --git a/ext/librethinkdbxx/README.md b/ext/librethinkdbxx/README.md
new file mode 100644
index 00000000..92fa9136
--- /dev/null
+++ b/ext/librethinkdbxx/README.md
@@ -0,0 +1,72 @@
+# RethinkDB driver for C++
+
+This driver is compatible with RethinkDB 2.0. It is based on the
+official RethinkDB Python driver.
+
+* [RethinkDB server](http://rethinkdb.com/)
+* [RethinkDB API docs](http://rethinkdb.com/api/python/)
+
+## Example
+
+```
+#include <memory>
+#include <cstdio>
+#include <rethinkdb.h>
+
+namespace R = RethinkDB;
+
+int main() {
+ std::unique_ptr<R::Connection> conn = R::connect("localhost", 28015);
+ R::Cursor cursor = R::table("users").filter(R::row["age"] > 14).run(*conn);
+ for (R::Datum& user : cursor) {
+ printf("%s\n", user.as_json().c_str());
+ }
+}
+```
+
+## Build
+
+Requires a modern C++ compiler. to build and install, run:
+
+```
+make
+make install
+```
+
+Will build `include/rethinkdb.h`, `librethinkdb++.a` and
+`librethinkdb++.so` into the `build/` directory.
+
+To include documentation from the Python driver in the header file,
+pass the following argument to make.
+
+```
+make INCLUDE_PYTHON_DOCS=yes
+```
+
+To build in debug mode:
+
+```
+make DEBUG=yes
+```
+
+To install to a specific location:
+
+```
+make install prefix=/usr/local DESTDIR=
+```
+
+## Status
+
+Still in early stages of development.
+
+## Tests
+
+This driver is tested against the upstream ReQL tests from the
+RethinkDB repo, which are programmatically translated from Python to
+C++. As of 34dc13c, all tests pass:
+
+```
+$ make test
+...
+SUCCESS: 2053 tests passed
+```
diff --git a/ext/librethinkdbxx/reql/add_docs.py b/ext/librethinkdbxx/reql/add_docs.py
new file mode 100644
index 00000000..67f08df8
--- /dev/null
+++ b/ext/librethinkdbxx/reql/add_docs.py
@@ -0,0 +1,80 @@
+from sys import stdin, stderr, stdout, argv
+from re import match, sub
+
+docs = {}
+
+for line in open(argv[1]):
+ res = match('^\t\(([^,]*), (.*)\),$', line)
+ if res:
+ fullname = res.group(1)
+ docs[fullname.split('.')[-1]] = eval(res.group(2)).decode('utf-8')
+
+translate_name = {
+ 'name': None,
+ 'delete_': 'delete',
+ 'union_': 'union',
+ 'operator[]': '__getitem__',
+ 'operator+': '__add__',
+ 'operator-': '__sub__',
+ 'operator*': '__mul__',
+ 'operator/': '__div__',
+ 'operator%': '__mod__',
+ 'operator&&': 'and_',
+ 'operator||': 'or_',
+ 'operator==': '__eq__',
+ 'operator!=': '__ne__',
+ 'operator>': '__gt__',
+ 'operator>=': '__ge__',
+ 'operator<': '__lt__',
+ 'operator<=': '__le__',
+ 'operator!': 'not_',
+ 'default_': 'default',
+ 'array': None,
+ 'desc': None,
+ 'asc': None,
+ 'maxval': None,
+ 'minval': None,
+ 'january': None,
+ 'february': None,
+ 'march': None,
+ 'april': None,
+ 'may': None,
+ 'june': None,
+ 'july': None,
+ 'august': None,
+ 'september': None,
+ 'october': None,
+ 'november': None,
+ 'december': None,
+ 'monday': None,
+ 'tuesday': None,
+ 'wednesday': None,
+ 'thursday': None,
+ 'friday': None,
+ 'saturday': None,
+ 'sunday': None,
+}
+
+def print_docs(name, line):
+ py_name = translate_name.get(name, name)
+ if py_name in docs:
+ indent = match("^( *)", line).group(1)
+ stdout.write('\n')
+ # TODO: convert the examples to C++
+ for line in docs[py_name].split('\n'):
+ stdout.write(indent + "// " + line + '\n')
+ elif py_name:
+ stderr.write('Warning: no docs for ' + py_name + ': ' + line)
+
+stdout.write('// Contains documentation copied as-is from the Python driver')
+
+for line in stdin:
+ res = match("^ *CO?[0-9_]+\(([^,)]+)|extern Query (\w+)|^ *// *(\$)doc\((\w+)\) *$", line)
+ if res:
+ name = res.group(1) or res.group(2) or res.group(4)
+ print_docs(name, line)
+ if not res.group(3):
+ stdout.write(line)
+ else:
+ stdout.write(line)
+
diff --git a/ext/librethinkdbxx/reql/gen.py b/ext/librethinkdbxx/reql/gen.py
new file mode 100644
index 00000000..2b1fe9fc
--- /dev/null
+++ b/ext/librethinkdbxx/reql/gen.py
@@ -0,0 +1,33 @@
+from sys import argv
+from re import sub, finditer, VERBOSE
+
+def gen(defs):
+ indent = 0
+ enum = False
+ def p(s): print(" " * (indent * 4) + s)
+ for item in finditer("""
+ (?P<type> message|enum) \\s+ (?P<name> \\w+) \\s* \\{ |
+ (?P<var> \\w+) \\s* = \\s* (?P<val> \\w+) \\s* ; |
+ \\}
+ """, defs, flags=VERBOSE):
+ if item.group(0) == "}":
+ indent = indent - 1
+ p("};" if enum else "}")
+ enum = False;
+ elif item.group('type') == 'enum':
+ p("enum class %s {" % item.group('name'))
+ indent = indent + 1
+ enum = True
+ elif item.group('type') == 'message':
+ p("namespace %s {" % item.group('name'))
+ indent = indent + 1
+ enum = False
+ else:
+ if enum:
+ p("%s = %s," % (item.group('var'), item.group('val')))
+
+print("// Auto-generated by reql/gen.py")
+print("#pragma once")
+print("namespace RethinkDB { namespace Protocol {")
+gen(sub("//.*", "", open(argv[1]).read()))
+print("} }")
diff --git a/ext/librethinkdbxx/reql/python_docs.txt b/ext/librethinkdbxx/reql/python_docs.txt
new file mode 100644
index 00000000..41a5c84e
--- /dev/null
+++ b/ext/librethinkdbxx/reql/python_docs.txt
@@ -0,0 +1,189 @@
+# This file was generated by _scripts/gen_python.py from the rethinkdb documentation in http://github.com/rethinkdb/docs
+# hash: "3d13a937cfdacb7ffa3dab2ce3ebdf25bd3c192e"
+
+import rethinkdb
+
+docsSource = [
+
+ (rethinkdb.net.Connection.close, b'conn.close(noreply_wait=True)\n\nClose an open connection.\n\nClosing a connection normally waits until all outstanding requests have finished and then frees any open resources associated with the connection. By passing `False` to the `noreply_wait` optional argument, the connection will be closed immediately, possibly aborting any outstanding noreply writes.\n\nA noreply query is executed by passing the `noreply` option to the [run](http://rethinkdb.com/api/python/run/) command, indicating that `run()` should not wait for the query to complete before returning. You may also explicitly wait for a noreply query to complete by using the [noreply_wait](http://rethinkdb.com/api/python/noreply_wait) command.\n\n*Example* Close an open connection, waiting for noreply writes to finish.\n\n conn.close()\n\n*Example* Close an open connection immediately.\n\n conn.close(noreply_wait=False)\n'),
+ (rethinkdb.connect, b'r.connect(host="localhost", port=28015, db="test", auth_key="", timeout=20) -> connection\nr.connect(host) -> connection\n\nCreate a new connection to the database server. The keyword arguments are:\n\n- `host`: host of the RethinkDB instance. The default value is `localhost`.\n- `port`: the driver port, by default `28015`.\n- `db`: the database used if not explicitly specified in a query, by default `test`.\n- `auth_key`: the authentication key, by default the empty string.\n- `timeout`: timeout period in seconds for the connection to be opened (default `20`).\n\nIf the connection cannot be established, a `RqlDriverError` exception will be thrown.\n\nThe authentication key can be set from the RethinkDB command line tool. Once set, client connections must provide the key as an option to `run` in order to make the connection. For more information, read "Using the RethinkDB authentication system" in the documentation on [securing your cluster](http://rethinkdb.com/docs/security/).\n\n__Note:__ Currently, the Python driver is not thread-safe. Each thread or multiprocessing PID should be given its own connection object. (This is likely to change in a future release of RethinkDB; you can track issue [#2427](https://github.com/rethinkdb/rethinkdb/issues/2427) for progress.)\n\n*Example* Opens a connection using the default host and port but specifying the default database.\n\n conn = r.connect(db=\'marvel\')\n\n*Example* Opens a new connection to the database.\n\n conn = r.connect(host = \'localhost\',\n port = 28015,\n db = \'heroes\',\n auth_key = \'hunter2\')\n\n'),
+ (rethinkdb.net.Connection.noreply_wait, b'conn.noreply_wait()\n\n`noreply_wait` ensures that previous queries with the `noreply` flag have been processed\nby the server. Note that this guarantee only applies to queries run on the given connection.\n\n*Example* We have previously run queries with the `noreply` argument set to `True`. Now\nwait until the server has processed them.\n\n conn.noreply_wait()\n\n'),
+ (rethinkdb, b'r -> r\n\nThe top-level ReQL namespace.\n\n*Example* Setup your top-level namespace.\n\n import rethinkdb as r\n\n'),
+ (rethinkdb.net.Connection.reconnect, b'conn.reconnect(noreply_wait=True)\n\nClose and reopen a connection.\n\nClosing a connection normally waits until all outstanding requests have finished and then frees any open resources associated with the connection. By passing `False` to the `noreply_wait` optional argument, the connection will be closed immediately, possibly aborting any outstanding noreply writes.\n\nA noreply query is executed by passing the `noreply` option to the [run](http://rethinkdb.com/api/python/run/) command, indicating that `run()` should not wait for the query to complete before returning. You may also explicitly wait for a noreply query to complete by using the [noreply_wait](http://rethinkdb.com/api/python/noreply_wait) command.\n\n*Example* Cancel outstanding requests/queries that are no longer needed.\n\n conn.reconnect(noreply_wait=False)\n'),
+ (rethinkdb.net.Connection.repl, b"conn.repl()\n\nSet the default connection to make REPL use easier. Allows calling\n`.run()` on queries without specifying a connection.\n\n__Note:__ Avoid using `repl` in application code. RethinkDB connection objects are not thread-safe, and calls to `connect` from multiple threads may change the global connection object used by `repl`. Applications should specify connections explicitly.\n\n*Example* Set the default connection for the REPL, then call\n`run()` without specifying the connection.\n\n r.connect(db='marvel').repl()\n r.table('heroes').run()\n"),
+ (rethinkdb.ast.RqlQuery.run, b'query.run(conn, use_outdated=False, time_format=\'native\', profile=False, durability="hard") -> cursor\nquery.run(conn, use_outdated=False, time_format=\'native\', profile=False, durability="hard") -> object\n\nRun a query on a connection, returning either a single JSON result or\na cursor, depending on the query.\n\nThe optional arguments are:\n\n- `use_outdated`: whether or not outdated reads are OK (default: `False`).\n- `time_format`: what format to return times in (default: `\'native\'`).\n Set this to `\'raw\'` if you want times returned as JSON objects for exporting.\n- `profile`: whether or not to return a profile of the query\'s\n execution (default: `False`).\n- `durability`: possible values are `\'hard\'` and `\'soft\'`. In soft durability mode RethinkDB\nwill acknowledge the write immediately after receiving it, but before the write has\nbeen committed to disk.\n- `group_format`: what format to return `grouped_data` and `grouped_streams` in (default: `\'native\'`).\n Set this to `\'raw\'` if you want the raw pseudotype.\n- `noreply`: set to `True` to not receive the result object or cursor and return immediately.\n- `db`: the database to run this query against as a string. The default is the database specified in the `db` parameter to [connect](http://rethinkdb.com/api/python/connect/) (which defaults to `test`). The database may also be specified with the [db](http://rethinkdb.com/api/python/db/) command.\n- `array_limit`: the maximum numbers of array elements that can be returned by a query (default: 100,000). This affects all ReQL commands that return arrays. Note that it has no effect on the size of arrays being _written_ to the database; those always have an upper limit of 100,000 elements.\n- `binary_format`: what format to return binary data in (default: `\'native\'`). Set this to `\'raw\'` if you want the raw pseudotype.\n- `min_batch_rows`: minimum number of rows to wait for before batching a result set (default: 8). This is an integer.\n- `max_batch_rows`: maximum number of rows to wait for before batching a result set (default: unlimited). This is an integer.\n- `max_batch_bytes`: maximum number of bytes to wait for before batching a result set (default: 1024). This is an integer.\n- `max_batch_seconds`: maximum number of seconds to wait before batching a result set (default: 0.5). This is a float (not an integer) and may be specified to the microsecond.\n- `first_batch_scaledown_factor`: factor to scale the other parameters down by on the first batch (default: 4). For example, with this set to 8 and `max_batch_rows` set to 80, on the first batch `max_batch_rows` will be adjusted to 10 (80 / 8). This allows the first batch to return faster.\n\n*Example* Run a query on the connection `conn` and print out every\nrow in the result.\n\n for doc in r.table(\'marvel\').run(conn):\n print doc\n\n*Example* If you are OK with potentially out of date data from all\nthe tables involved in this query and want potentially faster reads,\npass a flag allowing out of date data in an options object. Settings\nfor individual tables will supercede this global setting for all\ntables in the query.\n\n r.table(\'marvel\').run(conn, use_outdated=True)\n\n*Example* If you just want to send a write and forget about it, you\ncan set `noreply` to true in the options. In this case `run` will\nreturn immediately.\n\n r.table(\'marvel\').run(conn, noreply=True)\n\n*Example* If you want to specify whether to wait for a write to be\nwritten to disk (overriding the table\'s default settings), you can set\n`durability` to `\'hard\'` or `\'soft\'` in the options.\n\n r.table(\'marvel\')\n .insert({ \'superhero\': \'Iron Man\', \'superpower\': \'Arc Reactor\' })\n .run(conn, noreply=True, durability=\'soft\')\n\n*Example* If you do not want a time object to be converted to a\nnative date object, you can pass a `time_format` flag to prevent it\n(valid flags are "raw" and "native"). This query returns an object\nwith two fields (`epoch_time` and `$reql_type$`) instead of a native date\nobject.\n\n r.now().run(conn, time_format="raw")\n\n*Example* Specify the database to use for the query.\n\n for doc in r.table(\'marvel\').run(conn, db=\'heroes\'):\n print doc\n\nThis is equivalent to using the `db` command to specify the database:\n\n r.db(\'heroes\').table(\'marvel\').run(conn) ...\n\n*Example* Change the batching parameters for this query.\n\n r.table(\'marvel\').run(conn, max_batch_rows=16, max_batch_bytes=2048)\n'),
+ (rethinkdb.set_loop_type, b'r.set_loop_type(string)\n\nSet an asynchronous event loop model. Currently, the only event loop model RethinkDB supports is `"tornado"`, for use with the [Tornado web framework](http://www.tornadoweb.org). After setting the event loop to `"tornado"`, the [connect](http://rethinkdb.com/api/python/connect) and [run](http://rethinkdb.com/api/python/run) commands will return Tornado `Future` objects.\n\n*Example* Read a table\'s data using Tornado.\n\n r.set_loop_type("tornado")\n conn = r.connect(host=\'localhost\', port=28015)\n \n @gen.coroutine\n def use_cursor(conn):\n # Print every row in the table.\n cursor = yield r.table(\'test\').order_by(index="id").run(yield conn)\n while (yield cursor.fetch_next()):\n item = yield cursor.next()\n print(item)\n\nFor a longer discussion with Tornado examples, see the documentation article on [Asynchronous connections][ac].\n\n[ac]: /docs/async-connections/\n'),
+ (rethinkdb.net.Connection.use, b"conn.use(db_name)\n\nChange the default database on this connection.\n\n*Example* Change the default database so that we don't need to\nspecify the database when referencing a table.\n\n conn.use('marvel')\n r.table('heroes').run(conn) # refers to r.db('marvel').table('heroes')\n"),
+ (rethinkdb.ast.Table.config, b'table.config() -> selection&lt;object&gt;\ndatabase.config() -> selection&lt;object&gt;\n\nQuery (read and/or update) the configurations for individual tables or databases.\n\nThe `config` command is a shorthand way to access the `table_config` or `db_config` [System tables](http://rethinkdb.com/docs/system-tables/). It will return the single row from the system that corresponds to the database or table configuration, as if [get](http://rethinkdb.com/api/python/get) had been called on the system table with the UUID of the database or table in question.\n\n*Example* Get the configuration for the `users` table.\n\n r.table(\'users\').config().run(conn)\n \n {\n "id": "31c92680-f70c-4a4b-a49e-b238eb12c023",\n "name": "users",\n "db": "superstuff",\n "primary_key": "id",\n "shards": [\n {"primary_replica": "a", "replicas": ["a", "b"]},\n {"primary_replica": "d", "replicas": ["c", "d"]}\n ],\n "write_acks": "majority",\n "durability": "hard"\n }\n\n*Example* Change the write acknowledgement requirement of the `users` table.\n\n r.table(\'users\').config().update({\'write_acks\': \'single\'}).run(conn)\n'),
+ (rethinkdb.ast.Table.rebalance, b'table.rebalance() -> object\ndatabase.rebalance() -> object\n\nRebalances the shards of a table. When called on a database, all the tables in that database will be rebalanced.\n\nThe `rebalance` command operates by measuring the distribution of primary keys within a table and picking split points that will give each shard approximately the same number of documents. It won\'t change the number of shards within a table, or change any other configuration aspect for the table or the database.\n\nA table will lose availability temporarily after `rebalance` is called; use the [wait](http://rethinkdb.com/api/python/wait) command to wait for the table to become available again, or [status](http://rethinkdb.com/api/python/status) to check if the table is available for writing.\n\nRethinkDB automatically rebalances tables when the number of shards are increased, and as long as your documents have evenly distributed primary keys&mdash;such as the default UUIDs&mdash;it is rarely necessary to call `rebalance` manually. Cases where `rebalance` may need to be called include:\n\n* Tables with unevenly distributed primary keys, such as incrementing integers\n* Changing a table\'s primary key type\n* Increasing the number of shards on an empty table, then using non-UUID primary keys in that table\n\nThe [web UI](http://rethinkdb.com/docs/administration-tools/) (and the [info](http://rethinkdb.com/api/python/info) command) can be used to tell you when a table\'s shards need to be rebalanced.\n\nThe return value of `rebalance` is an object with two fields:\n\n* `rebalanced`: the number of tables rebalanced.\n* `status_changes`: a list of new and old table status values. Each element of the list will be an object with two fields:\n * `old_val`: The table\'s [status](http://rethinkdb.com/api/python/status) value before `rebalance` was executed. \n * `new_val`: The table\'s `status` value after `rebalance` was executed. (This value will almost always indicate the table is unavailable.)\n\nSee the [status](http://rethinkdb.com/api/python/status) command for an explanation of the objects returned in the `old_val` and `new_val` fields.\n\n*Example* Rebalance a table.\n\n r.table(\'superheroes\').rebalance().run(conn)\n \n {\n "rebalanced": 1,\n "status_changes": [\n {\n "old_val": {\n "db": "database",\n "id": "5cb35225-81b2-4cec-9eef-bfad15481265",\n "name": "superheroes",\n "shards": [\n {\n "primary_replica": "jeeves",\n "replicas": [\n {\n "server": "jeeves",\n "state": "ready"\n }\n ]\n },\n {\n "primary_replica": "jeeves",\n "replicas": [\n {\n "server": "jeeves",\n "state": "ready"\n }\n ]\n }\n ],\n "status": {\n "all_replicas_ready": True,\n "ready_for_outdated_reads": True,\n "ready_for_reads": True,\n "ready_for_writes": True\n }\n },\n "new_val": {\n "db": "database",\n "id": "5cb35225-81b2-4cec-9eef-bfad15481265",\n "name": "superheroes",\n "shards": [\n {\n "primary_replica": "jeeves",\n "replicas": [\n {\n "server": "jeeves",\n "state": "transitioning"\n }\n ]\n },\n {\n "primary_replica": "jeeves",\n "replicas": [\n {\n "server": "jeeves",\n "state": "transitioning"\n }\n ]\n }\n ],\n "status": {\n "all_replicas_ready": False,\n "ready_for_outdated_reads": False,\n "ready_for_reads": False,\n "ready_for_writes": False\n }\n }\n \n }\n ]\n }\n'),
+ (rethinkdb.ast.Table.reconfigure, b'table.reconfigure(shards=<s>, replicas=<r>[, primary_replica_tag=<t>, dry_run=False]) -> object\ndatabase.reconfigure(shards=<s>, replicas=<r>[, primary_replica_tag=<t>, dry_run=False]) -> object\n\nReconfigure a table\'s sharding and replication.\n\n* `shards`: the number of shards, an integer from 1-32. Required.\n* `replicas`: either an integer or a mapping object. Required.\n * If `replicas` is an integer, it specifies the number of replicas per shard. Specifying more replicas than there are servers will return an error.\n * If `replicas` is an object, it specifies key-value pairs of server tags and the number of replicas to assign to those servers: `{"tag1": 2, "tag2": 4, "tag3": 2, ...}`. For more information about server tags, read [Administration tools](http://rethinkdb.com/docs/administration-tools/).\n* `primary_replica_tag`: the primary server specified by its server tag. Required if `replicas` is an object; the tag must be in the object. This must *not* be specified if `replicas` is an integer.\n* `dry_run`: if `True` the generated configuration will not be applied to the table, only returned.\n\nThe return value of `reconfigure` is an object with three fields:\n\n* `reconfigured`: the number of tables reconfigured. This will be `0` if `dry_run` is `True`.\n* `config_changes`: a list of new and old table configuration values. Each element of the list will be an object with two fields:\n * `old_val`: The table\'s [config](http://rethinkdb.com/api/python/config) value before `reconfigure` was executed. \n * `new_val`: The table\'s `config` value after `reconfigure` was executed.\n* `status_changes`: a list of new and old table status values. Each element of the list will be an object with two fields:\n * `old_val`: The table\'s [status](http://rethinkdb.com/api/python/status) value before `reconfigure` was executed. \n * `new_val`: The table\'s `status` value after `reconfigure` was executed.\n\nFor `config_changes` and `status_changes`, see the [config](http://rethinkdb.com/api/python/config) and [status](http://rethinkdb.com/api/python/status) commands for an explanation of the objects returned in the `old_val` and `new_val` fields.\n\nA table will lose availability temporarily after `reconfigure` is called; use the [table_status](http://rethinkdb.com/api/python/table_status) command to determine when the table is available again.\n\n**Note:** Whenever you call `reconfigure`, the write durability will be set to `hard` and the write acknowledgments will be set to `majority`; these can be changed by using the `config` command on the table.\n\nIf `reconfigure` is called on a database, all the tables in the database will have their configurations affected. The return value will be an array of the objects described above, one per table.\n\nRead [Sharding and replication](http://rethinkdb.com/docs/sharding-and-replication/) for a complete discussion of the subject, including advanced topics.\n\n*Example* Reconfigure a table.\n\n r.table(\'superheroes\').reconfigure(shards=2, replicas=1).run(conn)\n \n {\n "reconfigured": 1,\n "config_changes": [\n {\n "new_val": {\n "id": "31c92680-f70c-4a4b-a49e-b238eb12c023",\n "name": "superheroes",\n "db": "superstuff",\n "primary_key": "id",\n "shards": [\n {"primary_replica": "jeeves", "replicas": ["jeeves"]},\n {"primary_replica": "alfred", "replicas": ["alfred"]}\n ],\n "write_acks": "majority",\n "durability": "hard"\n },\n "old_val": {\n "id": "31c92680-f70c-4a4b-a49e-b238eb12c023",\n "name": "superheroes",\n "db": "superstuff",\n "primary_key": "id",\n "shards": [\n {"primary_replica": "alfred", "replicas": ["alfred"]}\n ],\n "write_acks": "majority",\n "durability": "hard"\n }\n }\n ],\n "status_changes": [\n {\n "new_val": (status object),\n "old_val": (status object)\n }\n ]\n }\n\n*Example* Reconfigure a table, specifying replicas by server tags.\n\n r.table(\'superheroes\').reconfigure(shards=2, replicas={\'wooster\': 1, \'wayne\': 1}, primary_replica_tag=\'wooster\').run(conn)\n \n {\n "reconfigured": 1,\n "config_changes": [\n {\n "new_val": {\n "id": "31c92680-f70c-4a4b-a49e-b238eb12c023",\n "name": "superheroes",\n "db": "superstuff",\n "primary_key": "id",\n "shards": [\n {"primary_replica": "jeeves", "replicas": ["jeeves", "alfred"]},\n {"primary_replica": "jeeves", "replicas": ["jeeves", "alfred"]}\n ],\n "write_acks": "majority",\n "durability": "hard"\n },\n "old_val": {\n "id": "31c92680-f70c-4a4b-a49e-b238eb12c023",\n "name": "superheroes",\n "db": "superstuff",\n "primary_key": "id",\n "shards": [\n {"primary_replica": "alfred", "replicas": ["alfred"]}\n ],\n "write_acks": "majority",\n "durability": "hard"\n }\n }\n ],\n "status_changes": [\n {\n "new_val": (status object),\n "old_val": (status object)\n }\n ]\n }\n'),
+ (rethinkdb.ast.Table.status, b'table.status() -> selection&lt;object&gt;\n\nReturn the status of a table.\n\nThe return value is an object providing information about the table\'s shards, replicas and replica readiness states. For a more complete discussion of the object fields, read about the `table_status` table in [System tables](http://rethinkdb.com/docs/system-tables/).\n\n* `db`: database name.\n* `name`: table name.\n* `id`: table UUID.\n* `shards`: an array of objects, one for each shard, with the following keys per object:\n * `primary_replica`: name of the shard\'s primary server.\n * `replicas`: an array of objects showing the status of each replica, with the following keys:\n * `server`: name of the replica server.\n * `state`: one of `ready`, `disconnected`, `backfilling_data`, `offloading_data`, `erasing_data`, `looking_for_primary_replica` or `transitioning`.\n* `status`: an object with the following boolean keys:\n * `all_replicas_ready`: `True` if all backfills have finished.\n * `ready_for_outdated_reads`: `True` if the table is ready for read queries with the `use_outdated` flag set to `True`.\n * `ready_for_reads`: `True` if the table is ready for read queries with current data (with the `use_outdated` flag set to `False` or unspecified).\n * `ready_for_writes`: `True` if the table is ready for write queries.\n\n*Example* Get a table\'s status.\n\n r.table(\'superheroes\').status().run(conn)\n \n {\n "db": "database",\n "id": "5cb35225-81b2-4cec-9eef-bfad15481265",\n "name": "superheroes",\n "shards": [\n {\n "primary_replica": "jeeves",\n "replicas": [\n {\n "server": "jeeves",\n "state": "ready"\n }\n ]\n },\n {\n "primary_replica": "jeeves",\n "replicas": [\n {\n "server": "jeeves",\n "state": "ready"\n }\n ]\n }\n ],\n "status": {\n "all_replicas_ready": True,\n "ready_for_outdated_reads": True,\n "ready_for_reads": True,\n "ready_for_writes": True\n }\n }\n'),
+ (rethinkdb.ast.Table.wait, b'table.wait([wait_for=\'ready_for_writes\', timeout=<sec>]) -> object\ndatabase.wait([wait_for=\'ready_for_writes\', timeout=<sec>]) -> object\nr.wait([wait_for=\'ready_for_writes\', timeout=<sec>]) -> object\n\nWait for a table or all the tables in a database to be ready. A table may be temporarily unavailable after creation, rebalancing or reconfiguring. The `wait` command blocks until the given table (or database) is fully up to date.\n\nThe `wait` command takes two optional arguments:\n\n* `wait_for`: a string indicating a table [status](http://rethinkdb.com/api/python/status) to wait on before returning, one of `ready_for_outdated_reads`, `ready_for_reads`, `ready_for_writes`, or `all_replicas_ready`. The default is `ready_for_writes`. \n* `timeout`: a number indicating maximum time to wait for in seconds before returning. The default is no timeout.\n\nThe return value is an object consisting of two key/value pairs:\n\n* `ready`: an integer indicating the number of tables waited for. It will always be `1` when `wait` is called on a table, and the total number of tables when called on a database.\n* `status_changes`: a list with one entry for each of the tables. Each member of the list will be an object with two fields:\n * `old_val`: The table\'s [status](http://rethinkdb.com/api/python/status) value before `wait` was executed. \n * `new_val`: The table\'s `status` value after `wait` finished.\n\nSee [status](http://rethinkdb.com/api/python/status) and [System tables](http://rethinkdb.com/docs/system-tables/) for a description of the fields within `status_changes`.\n\nIf `wait` is called with no table or database specified (the `r.wait()` form), it will wait on all the tables in the default database (set with the [connect](http://rethinkdb.com/api/python/connect/) command\'s `db` parameter, which defaults to `test`).\n\n*Example* Wait on a table to be ready.\n\n r.table(\'superheroes\').wait().run(conn)\n \n {\n "ready": 1,\n "status_changes": [\n \t{\n \t "old_val": {\n \t\t"db": "database",\n \t\t"id": "5cb35225-81b2-4cec-9eef-bfad15481265",\n \t\t"name": "superheroes",\n \t\t"shards": [\n \t\t {\n \t\t\t"primary_replica": None,\n \t\t\t"replicas": [\n \t\t\t {\n \t\t\t\t"server": "jeeves",\n \t\t\t\t"state": "ready"\n \t\t\t }\n \t\t\t]\n \t\t },\n \t\t {\n \t\t\t"primary_replica": None,\n \t\t\t"replicas": [\n \t\t\t {\n \t\t\t\t"server": "jeeves",\n \t\t\t\t"state": "ready"\n \t\t\t }\n \t\t\t]\n \t\t }\n \t\t],\n \t\t"status": {\n \t\t "all_replicas_ready": True,\n \t\t "ready_for_outdated_reads": True,\n \t\t "ready_for_reads": True,\n \t\t "ready_for_writes": True\n \t\t}\n \t },\n \t "new_val": {\n \t\t"db": "database",\n \t\t"id": "5cb35225-81b2-4cec-9eef-bfad15481265",\n \t\t"name": "superheroes",\n \t\t"shards": [\n \t\t {\n \t\t\t"primary_replica": None,\n \t\t\t"replicas": [\n \t\t\t {\n \t\t\t\t"server": "jeeves",\n \t\t\t\t"state": "ready"\n \t\t\t }\n \t\t\t]\n \t\t },\n \t\t {\n \t\t\t"primary_replica": None,\n \t\t\t"replicas": [\n \t\t\t {\n \t\t\t\t"server": "jeeves",\n \t\t\t\t"state": "ready"\n \t\t\t }\n \t\t\t]\n \t\t }\n \t\t],\n \t\t"status": {\n \t\t "all_replicas_ready": True,\n \t\t "ready_for_outdated_reads": True,\n \t\t "ready_for_reads": True,\n \t\t "ready_for_writes": True\n \t\t}\n \t }\n \t}\n ]\n }\n'),
+ (rethinkdb.ast.RqlQuery.avg, b"sequence.avg([field_or_function]) -> number\n\nAverages all the elements of a sequence. If called with a field name,\naverages all the values of that field in the sequence, skipping\nelements of the sequence that lack that field. If called with a\nfunction, calls that function on every element of the sequence and\naverages the results, skipping elements of the sequence where that\nfunction returns `None` or a non-existence error.\n\nProduces a non-existence error when called on an empty sequence. You\ncan handle this case with `default`.\n\n*Example* What's the average of 3, 5, and 7?\n\n r.expr([3, 5, 7]).avg().run(conn)\n\n*Example* What's the average number of points scored in a game?\n\n r.table('games').avg('points').run(conn)\n\n*Example* What's the average number of points scored in a game,\ncounting bonus points?\n\n r.table('games').avg(lambda game:\n game['points'] + game['bonus_points']\n ).run(conn)\n\n*Example* What's the average number of points scored in a game?\n(But return `None` instead of raising an error if there are no games where\npoints have been scored.)\n\n r.table('games').avg('points').default(None).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.contains, b"sequence.contains(value|predicate[, value|predicate, ...]) -> bool\n\nWhen called with values, returns `True` if a sequence contains all the\nspecified values. When called with predicate functions, returns `True`\nif for each predicate there exists at least one element of the stream\nwhere that predicate returns `True`.\n\nValues and predicates may be mixed freely in the argument list.\n\n*Example* Has Iron Man ever fought Superman?\n\n r.table('marvel').get('ironman')['opponents'].contains('superman').run(conn)\n\n*Example* Has Iron Man ever defeated Superman in battle?\n\n r.table('marvel').get('ironman')['battles'].contains(lambda battle:\n (battle['winner'] == 'ironman') & (battle['loser'] == 'superman')\n ).run(conn)\n\n*Example* Use `contains` with a predicate function to simulate an `or`. Return the Marvel superheroes who live in Detroit, Chicago or Hoboken.\n\n r.table('marvel').filter(\n lambda hero: r.expr(['Detroit', 'Chicago', 'Hoboken']).contains(hero['city'])\n ).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.count, b"sequence.count([value_or_predicate]) -> number\nbinary.count() -> number\n\nCounts the number of elements in a sequence. If called with a value,\ncounts the number of times that value occurs in the sequence. If\ncalled with a predicate function, counts the number of elements in the\nsequence where that function returns `True`.\n\nIf `count` is called on a [binary](http://rethinkdb.com/api/python/binary) object, it will return the size of the object in bytes.\n\n*Example* Count the number of users.\n\n r.table('users').count().run(conn)\n\n*Example* Count the number of 18 year old users.\n\n r.table('users')['age'].count(18).run(conn)\n\n*Example* Count the number of users over 18.\n\n r.table('users')['age'].count(lambda age: age > 18).run(conn)\n\n r.table('users').count(lambda user: user['age'] > 18).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.distinct, b"sequence.distinct() -> array\ntable.distinct([index=<indexname>]) -> stream\n\nRemoves duplicate elements from a sequence.\n\nThe `distinct` command can be called on any sequence or table with an index.\n\n{% infobox %}\nWhile `distinct` can be called on a table without an index, the only effect will be to convert the table into a stream; the content of the stream will not be affected.\n{% endinfobox %}\n\n*Example* Which unique villains have been vanquished by Marvel heroes?\n\n r.table('marvel').concat_map(\n lambda hero: hero['villain_list']).distinct().run(conn)\n\n*Example* Topics in a table of messages have a secondary index on them, and more than one message can have the same topic. What are the unique topics in the table?\n\n r.table('messages').distinct(index='topics').run(conn)\n\nThe above structure is functionally identical to:\n\n r.table('messages')['topics'].distinct().run(conn)\n\nHowever, the first form (passing the index as an argument to `distinct`) is faster, and won't run into array limit issues since it's returning a stream.\n"),
+ (rethinkdb.ast.RqlQuery.group, b'sequence.group(field_or_function..., [index=\'index_name\', multi=False]) -> grouped_stream\n\nTakes a stream and partitions it into multiple groups based on the\nfields or functions provided.\n\nWith the `multi` flag single documents can be assigned to multiple groups, similar to the behavior of [multi-indexes](http://rethinkdb.com/docs/secondary-indexes/python). When `multi` is `True` and the grouping value is an array, documents will be placed in each group that corresponds to the elements of the array. If the array is empty the row will be ignored.\n\n*Example* Grouping games by player.\n\nSuppose that the table `games` has the following data:\n\n [\n {"id": 2, "player": "Bob", "points": 15, "type": "ranked"},\n {"id": 5, "player": "Alice", "points": 7, "type": "free"},\n {"id": 11, "player": "Bob", "points": 10, "type": "free"},\n {"id": 12, "player": "Alice", "points": 2, "type": "free"}\n ]\n\nGrouping games by player can be done with:\n\n > r.table(\'games\').group(\'player\').run(conn)\n \n {\n "Alice": [\n {"id": 5, "player": "Alice", "points": 7, "type": "free"},\n {"id": 12, "player": "Alice", "points": 2, "type": "free"}\n ],\n "Bob": [\n {"id": 2, "player": "Bob", "points": 15, "type": "ranked"},\n {"id": 11, "player": "Bob", "points": 10, "type": "free"}\n ]\n }\n\nCommands chained after `group` will be called on each of these grouped\nsub-streams, producing grouped data.\n\n*Example* What is each player\'s best game?\n\n > r.table(\'games\').group(\'player\').max(\'points\').run(conn)\n \n {\n "Alice": {"id": 5, "player": "Alice", "points": 7, "type": "free"},\n "Bob": {"id": 2, "player": "Bob", "points": 15, "type": "ranked"}\n }\n\nCommands chained onto grouped data will operate on each grouped datum,\nproducing more grouped data.\n\n*Example* What is the maximum number of points scored by each player?\n\n > r.table(\'games\').group(\'player\').max(\'points\')[\'points\'].run(conn)\n \n {\n "Alice": 7,\n "Bob": 15\n }\n\nYou can also group by more than one field.\n\n*Example* What is the maximum number of points scored by each\nplayer for each game type?\n\n > r.table(\'games\').group(\'player\', \'type\').max(\'points\')[\'points\'].run(conn)\n \n {\n ("Alice", "free"): 7,\n ("Bob", "free"): 10,\n ("Bob", "ranked"): 15\n }\n\nYou can also group by a function.\n\n*Example* What is the maximum number of points scored by each\nplayer for each game type?\n\n > r.table(\'games\')\n .group(lambda game:\n game.pluck(\'player\', \'type\')\n ).max(\'points\')[\'points\'].run(conn)\n \n {\n frozenset([(\'player\', \'Alice\'), (\'type\', \'free\')]): 7,\n frozenset([(\'player\', \'Bob\'), (\'type\', \'free\')]): 10,\n frozenset([(\'player\', \'Bob\'), (\'type\', \'ranked\')]): 15,\n }\n\nUsing a function, you can also group by date on a ReQL [date field](http://rethinkdb.com/docs/dates-and-times/javascript/).\n\n*Example* How many matches have been played this year by month?\n\n > r.table(\'matches\').group(\n lambda match: [match[\'date\'].year(), match[\'date\'].month()]\n ).count().run(conn)\n \n {\n (2014, 2): 2,\n (2014, 3): 2,\n (2014, 4): 1,\n (2014, 5): 3\n }\n\nYou can also group on an index (primary key or secondary).\n\n*Example* What is the maximum number of points scored by game type?\n\n > r.table(\'games\').group(index=\'type\').max(\'points\')[\'points\'].run(conn)\n \n {\n "free": 10,\n "ranked": 15\n }\n\nSuppose that the table `games2` has the following data:\n\n [\n { \'id\': 1, \'matches\': {\'a\': [1, 2, 3], \'b\': [4, 5, 6]} },\n { \'id\': 2, \'matches\': {\'b\': [100], \'c\': [7, 8, 9]} },\n { \'id\': 3, \'matches\': {\'a\': [10, 20], \'c\': [70, 80]} }\n ]\n\nUsing the `multi` option we can group data by match A, B or C.\n\n > r.table(\'games2\').group(r.row[\'matches\'].keys(), multi=True).run(conn)\n \n [\n {\n \'group\': \'a\',\n \'reduction\': [ <id 1>, <id 3> ]\n },\n {\n \'group\': \'b\',\n \'reduction\': [ <id 1>, <id 2> ]\n },\n {\n \'group\': \'c\',\n \'reduction\': [ <id 2>, <id 3> ]\n }\n ]\n\n(The full result set is abbreviated in the figure; `<id 1>, <id 2>` and `<id 3>` would be the entire documents matching those keys.)\n\n*Example* Use [map](http://rethinkdb.com/api/python/map) and [sum](http://rethinkdb.com/api/python/sum) to get the total points scored for each match.\n\n r.table(\'games2\').group(r.row[\'matches\'].keys(), multi=True).ungroup().map(\n lambda doc: { \'match\': doc[\'group\'], \'total\': doc[\'reduction\'].sum(\n lambda set: set[\'matches\'][doc[\'group\']].sum()\n )}).run(conn)\n \n [\n { \'match\': \'a\', \'total\': 36 },\n { \'match\': \'b\', \'total\': 115 },\n { \'match\': \'c\', \'total\': 174 }\n ]\n\nThe inner `sum` adds the scores by match within each document; the outer `sum` adds those results together for a total across all the documents.\n\nIf you want to operate on all the groups rather than operating on each\ngroup (e.g. if you want to order the groups by their reduction), you\ncan use [ungroup](http://rethinkdb.com/api/python/ungroup/) to turn a grouped stream or\ngrouped data into an array of objects representing the groups.\n\n*Example* Ungrouping grouped data.\n\n > r.table(\'games\').group(\'player\').max(\'points\')[\'points\'].ungroup().run(conn)\n \n [\n {\n "group": "Alice",\n "reduction": 7\n },\n {\n "group": "Bob",\n "reduction": 15\n }\n ]\n\nUngrouping is useful e.g. for ordering grouped data, or for inserting\ngrouped data into a table.\n\n*Example* What is the maximum number of points scored by each\nplayer, with the highest scorers first?\n\n > r.table(\'games\').group(\'player\').max(\'points\')[\'points\'].ungroup().order_by(\n r.desc(\'reduction\')).run(conn)\n \n [\n {\n "group": "Bob",\n "reduction": 15\n },\n {\n "group": "Alice",\n "reduction": 7\n }\n ]\n\nWhen grouped data are returned to the client, they are transformed\ninto a client-specific native type. (Something similar is done with\n[times](http://rethinkdb.com/docs/dates-and-times/).) In Python, grouped data are\ntransformed into a `dictionary`. If the group value is an `array`, the\nkey is converted to a `tuple`. If the group value is a `dictionary`,\nit will be converted to a `frozenset`.\n\nIf you instead want to receive the raw\npseudotype from the server (e.g. if you\'re planning to serialize the\nresult as JSON), you can specify `group_format: \'raw\'` as an optional\nargument to `run`:\n\n*Example* Get back the raw `GROUPED_DATA` pseudotype.\n\n > r.table(\'games\').group(\'player\').avg(\'points\').run(conn, group_format=\'raw\')\n \n {\n "$reql_type$": "GROUPED_DATA",\n "data": [\n ["Alice", 4.5],\n ["Bob", 12.5]\n ]\n }\n\nNot passing the `group_format` flag would return:\n\n {\n "Alice": 4.5,\n "Bob": 12.5\n }\n\nYou might also want to use the [ungroup](http://rethinkdb.com/api/python/ungroup/)\ncommand (see above), which will turn the grouped data into an array of\nobjects on the server.\n\nIf you run a query that returns a grouped stream, it will be\nautomatically converted to grouped data before being sent back to you\n(there is currently no efficient way to stream groups from RethinkDB).\nThis grouped data is subject to the array size limit (see [run](http://rethinkdb.com/api/python/run)).\n\nIn general, operations on grouped streams will be efficiently\ndistributed, and operations on grouped data won\'t be. You can figure\nout what you\'re working with by putting `type_of` on the end of your\nquery. Below are efficient and inefficient examples.\n\n*Example* Efficient operation.\n\n # r.table(\'games\').group(\'player\').type_of().run(conn)\n # Returns "GROUPED_STREAM"\n r.table(\'games\').group(\'player\').min(\'points\').run(conn) # EFFICIENT\n\n*Example* Inefficient operation.\n\n # r.table(\'games\').group(\'player\').order_by(\'score\').type_of().run(conn)\n # Returns "GROUPED_DATA"\n r.table(\'games\').group(\'player\').order_by(\'score\').nth(0).run(conn) # INEFFICIENT\n\nWhat does it mean to be inefficient here? When operating on grouped\ndata rather than a grouped stream, *all* of the data has to be\navailable on the node processing the query. This means that the\noperation will only use one server\'s resources, and will require\nmemory proportional to the size of the grouped data it\'s operating\non. (In the case of the `order_by` in the inefficient example, that\nmeans memory proportional **to the size of the table**.) The array\nlimit is also enforced for grouped data, so the `order_by` example\nwould fail for tables with more than 100,000 rows unless you used the `array_limit` option with `run`.\n\n*Example* What is the maximum number of points scored by each\nplayer in free games?\n\n > r.table(\'games\').filter(lambda game:\n game[\'type\'] = \'free\'\n ).group(\'player\').max(\'points\')[\'points\'].run(conn)\n \n {\n "Alice": 7,\n "Bob": 10\n }\n\n*Example* What is each player\'s highest even and odd score?\n\n > r.table(\'games\')\n .group(\'name\', lambda game:\n game[\'points\'] % 2\n ).max(\'points\')[\'points\'].run(conn)\n \n {\n ("Alice", 1): 7,\n ("Bob", 0): 10,\n ("Bob", 1): 15\n }\n'),
+ (rethinkdb.ast.RqlQuery.max, b"sequence.max(field_or_function) -> element\nsequence.max(index='index') -> element\n\nFinds the maximum element of a sequence. The `max` command can be called with:\n\n* a **field name**, to return the element of the sequence with the largest value in that field;\n* an **index** (the primary key or a secondary index), to return the element of the sequence with the largest value in that index;\n* a **function**, to apply the function to every element within the sequence and return the element which returns the largest value from the function, ignoring any elements where the function produces a non-existence error.\n\nFor more information on RethinkDB's sorting order, read the section in [ReQL data types](http://rethinkdb.com/docs/data-types/#sorting-order).\n\nCalling `max` on an empty sequence will throw a non-existence error; this can be handled using the [default](http://rethinkdb.com/api/python/default/) command.\n\n*Example* Return the maximum value in the list `[3, 5, 7]`.\n\n r.expr([3, 5, 7]).max().run(conn)\n\n*Example* Return the user who has scored the most points.\n\n r.table('users').max('points').run(conn)\n\n*Example* The same as above, but using a secondary index on the `points` field.\n\n r.table('users').max(index='points').run(conn)\n\n*Example* Return the user who has scored the most points, adding in bonus points from a separate field using a function.\n\n r.table('users').max(lambda user:\n user['points'] + user['bonus_points']\n ).run(conn)\n\n*Example* Return the highest number of points any user has ever scored. This returns the value of that `points` field, not a document.\n\n r.table('users').max('points')['points'].run(conn)\n\n*Example* Return the user who has scored the most points, but add a default `None` return value to prevent an error if no user has ever scored points.\n\n r.table('users').max('points').default(None).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.min, b"sequence.min(field_or_function) -> element\nsequence.min(index='index') -> element\n\nFinds the minimum element of a sequence. The `min` command can be called with:\n\n* a **field name**, to return the element of the sequence with the smallest value in that field;\n* an **index** (the primary key or a secondary index), to return the element of the sequence with the smallest value in that index;\n* a **function**, to apply the function to every element within the sequence and return the element which returns the smallest value from the function, ignoring any elements where the function produces a non-existence error.\n\nFor more information on RethinkDB's sorting order, read the section in [ReQL data types](http://rethinkdb.com/docs/data-types/#sorting-order).\n\nCalling `min` on an empty sequence will throw a non-existence error; this can be handled using the [default](http://rethinkdb.com/api/python/default/) command.\n\n*Example* Return the minimum value in the list `[3, 5, 7]`.\n\n r.expr([3, 5, 7]).min().run(conn)\n\n*Example* Return the user who has scored the fewest points.\n\n r.table('users').min('points').run(conn)\n\n*Example* The same as above, but using a secondary index on the `points` field.\n\n r.table('users').min(index='points').run(conn)\n\n*Example* Return the user who has scored the fewest points, adding in bonus points from a separate field using a function.\n\n r.table('users').min(lambda user:\n user['points'] + user['bonus_points']\n ).run(conn)\n\n*Example* Return the smallest number of points any user has ever scored. This returns the value of that `points` field, not a document.\n\n r.table('users').min('points')['points'].run(conn)\n\n*Example* Return the user who has scored the fewest points, but add a default `None` return value to prevent an error if no user has ever scored points.\n\n r.table('users').min('points').default(None).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.reduce, b'sequence.reduce(reduction_function) -> value\n\nProduce a single value from a sequence through repeated application of a reduction\nfunction. \nThe reduction function can be called on:\n\n- two elements of the sequence\n- one element of the sequence and one result of a previous reduction\n- two results of previous reductions\n\nThe reduction function can be called on the results of two previous reductions because the\n`reduce` command is distributed and parallelized across shards and CPU cores. A common\nmistaken when using the `reduce` command is to suppose that the reduction is executed\nfrom left to right. Read the [map-reduce in RethinkDB](http://rethinkdb.com/docs/map-reduce/) article to\nsee an example.\n\nIf the sequence is empty, the server will produce a `RqlRuntimeError` that can be\ncaught with `default`. \nIf the sequence has only one element, the first element will be returned.\n\n*Example* Return the number of documents in the table `posts`.\n\n r.table("posts").map(lambda doc: 1)\n .reduce(lambda left, right: left+right)\n .default(0).run(conn)\n\nA shorter way to execute this query is to use [count](http://rethinkdb.com/api/python/count).\n\n*Example* Suppose that each `post` has a field `comments` that is an array of\ncomments. \nReturn the number of comments for all posts.\n\n r.table("posts").map(lambda doc:\n doc["comments"].count()\n ).reduce(lambda left, right:\n left+right\n ).default(0).run(conn)\n\n*Example* Suppose that each `post` has a field `comments` that is an array of\ncomments. \nReturn the maximum number comments per post.\n\n r.table("posts").map(lambda doc:\n doc["comments"].count()\n ).reduce(lambda left, right:\n r.branch(\n left > right,\n left,\n right\n )\n ).default(0).run(conn)\n\nA shorter way to execute this query is to use [max](http://rethinkdb.com/api/python/max).\n'),
+ (rethinkdb.ast.RqlQuery.sum, b"sequence.sum([field_or_function]) -> number\n\nSums all the elements of a sequence. If called with a field name,\nsums all the values of that field in the sequence, skipping elements\nof the sequence that lack that field. If called with a function,\ncalls that function on every element of the sequence and sums the\nresults, skipping elements of the sequence where that function returns\n`None` or a non-existence error.\n\nReturns `0` when called on an empty sequence.\n\n*Example* What's 3 + 5 + 7?\n\n r.expr([3, 5, 7]).sum().run(conn)\n\n*Example* How many points have been scored across all games?\n\n r.table('games').sum('points').run(conn)\n\n*Example* How many points have been scored across all games,\ncounting bonus points?\n\n r.table('games').sum(lambda game:\n game['points'] + game['bonus_points']\n ).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.ungroup, b'grouped_stream.ungroup() -> array\ngrouped_data.ungroup() -> array\n\nTakes a grouped stream or grouped data and turns it into an array of\nobjects representing the groups. Any commands chained after `ungroup`\nwill operate on this array, rather than operating on each group\nindividually. This is useful if you want to e.g. order the groups by\nthe value of their reduction.\n\nThe format of the array returned by `ungroup` is the same as the\ndefault native format of grouped data in the JavaScript driver and\ndata explorer.\n\n*Example* What is the maximum number of points scored by each\nplayer, with the highest scorers first?\n\nSuppose that the table `games` has the following data:\n\n [\n {"id": 2, "player": "Bob", "points": 15, "type": "ranked"},\n {"id": 5, "player": "Alice", "points": 7, "type": "free"},\n {"id": 11, "player": "Bob", "points": 10, "type": "free"},\n {"id": 12, "player": "Alice", "points": 2, "type": "free"}\n ]\n\nWe can use this query:\n\n r.table(\'games\')\n .group(\'player\').max(\'points\')[\'points\']\n .ungroup().order_by(r.desc(\'reduction\')).run(conn)\n\nResult: \n\n [\n {\n "group": "Bob",\n "reduction": 15\n },\n {\n "group": "Alice",\n "reduction": 7\n }\n ]\n\n*Example* Select one random player and all their games.\n\n r.table(\'games\').group(\'player\').ungroup().sample(1).run(conn)\n\nResult:\n\n [\n {\n "group": "Bob",\n "reduction": [\n {"id": 2, "player": "Bob", "points": 15, "type": "ranked"},\n {"id": 11, "player": "Bob", "points": 10, "type": "free"}\n ]\n }\n ]\n\nNote that if you didn\'t call `ungroup`, you would instead select one\nrandom game from each player:\n\n r.table(\'games\').group(\'player\').sample(1).run(conn)\n\nResult:\n\n {\n "Alice": [\n {"id": 5, "player": "Alice", "points": 7, "type": "free"}\n ],\n "Bob": [\n {"id": 11, "player": "Bob", "points": 10, "type": "free"}\n ]\n }\n\n*Example* Types!\n\n r.table(\'games\').group(\'player\').type_of().run(conn) # Returns "GROUPED_STREAM"\n r.table(\'games\').group(\'player\').ungroup().type_of().run(conn) # Returns "ARRAY"\n r.table(\'games\').group(\'player\').avg(\'points\').run(conn) # Returns "GROUPED_DATA"\n r.table(\'games\').group(\'player\').avg(\'points\').ungroup().run(conn) #Returns "ARRAY"\n'),
+ (rethinkdb.args, b"r.args(array) -> special\n\n`r.args` is a special term that's used to splice an array of arguments\ninto another term. This is useful when you want to call a variadic\nterm such as `get_all` with a set of arguments produced at runtime.\n\nThis is analogous to unpacking argument lists in Python.\n\n*Example* Get Alice and Bob from the table `people`.\n\n r.table('people').get_all('Alice', 'Bob').run(conn)\n # or\n r.table('people').get_all(r.args(['Alice', 'Bob'])).run(conn)\n\n*Example* Get all of Alice's children from the table `people`.\n\n # r.table('people').get('Alice') returns {'id': 'Alice', 'children': ['Bob', 'Carol']}\n r.table('people').get_all(r.args(r.table('people').get('Alice')['children'])).run(conn)\n"),
+ (rethinkdb.binary, b'r.binary(data) -> binary\n\nEncapsulate binary data within a query.\n\nThe type of data `binary` accepts depends on the client language. In Python, it expects a parameter of `bytes` type. Using a `bytes` object within a query implies the use of `binary` and the ReQL driver will automatically perform the coercion (in Python 3 only).\n\nBinary objects returned to the client in JavaScript will also be of the `bytes` type. This can be changed with the `binary_format` option provided to [run](http://rethinkdb.com/api/python/run) to return "raw" objects.\n\nOnly a limited subset of ReQL commands may be chained after `binary`:\n\n* [coerce_to](http://rethinkdb.com/api/python/coerce_to/) can coerce `binary` objects to `string` types\n* [count](http://rethinkdb.com/api/python/count/) will return the number of bytes in the object\n* [slice](http://rethinkdb.com/api/python/slice/) will treat bytes like array indexes (i.e., `slice(10,20)` will return bytes 10&ndash;19)\n* [type_of](http://rethinkdb.com/api/python/type_of) returns `PTYPE<BINARY>`\n* [info](http://rethinkdb.com/api/python/info) will return information on a binary object.\n\n*Example* Save an avatar image to a existing user record.\n\n f = open(\'./default_avatar.png\', \'rb\')\n avatar_image = f.read()\n f.close()\n r.table(\'users\').get(100).update({\'avatar\': r.binary(avatar_image)}).run(conn)\n\n*Example* Get the size of an existing avatar image.\n\n r.table(\'users\').get(100)[\'avatar\'].count().run(conn)\n \n 14156\n\nRead more details about RethinkDB\'s binary object support: [Storing binary objects](http://rethinkdb.com/docs/storing-binary/).\n'),
+ (rethinkdb.branch, b'r.branch(test, true_branch, false_branch) -> any\n\nIf the `test` expression returns `False` or `None`, the `false_branch` will be evaluated.\nOtherwise, the `true_branch` will be evaluated.\n \nThe `branch` command is effectively an `if` renamed due to language constraints.\n\n*Example* Return heroes and superheroes.\n\n r.table(\'marvel\').map(\n r.branch(\n r.row[\'victories\'] > 100,\n r.row[\'name\'] + \' is a superhero\',\n r.row[\'name\'] + \' is a hero\'\n )\n ).run(conn)\n\nIf the documents in the table `marvel` are:\n\n [{\n "name": "Iron Man",\n "victories": 214\n },\n {\n "name": "Jubilee",\n "victories": 9\n }]\n\nThe results will be:\n\n [\n "Iron Man is a superhero",\n "Jubilee is a hero"\n ]\n'),
+ (rethinkdb.ast.RqlQuery.coerce_to, b"sequence.coerce_to('array') -> array\nvalue.coerce_to('string') -> string\nstring.coerce_to('number') -> number\narray.coerce_to('object') -> object\nobject.coerce_to('array') -> array\nbinary.coerce_to('string') -> string\nstring.coerce_to('binary') -> binary\n\nConvert a value of one type into another.\n\n* a sequence, selection or object can be coerced to an array\n* an array of key-value pairs can be coerced to an object\n* a string can be coerced to a number\n* any datum (single value) can be coerced to a string\n* a binary object can be coerced to a string and vice-versa\n\n*Example* Coerce a stream to an array to store its output in a field. (A stream cannot be stored in a field directly.)\n\n r.table('posts').map(lambda post: post.merge(\n { 'comments': r.table('comments').get_all(post['id'], index='post_id').coerce_to('array') }\n )).run(conn)\n\n*Example* Coerce an array of pairs into an object.\n\n r.expr([['name', 'Ironman'], ['victories', 2000]]).coerce_to('object').run(conn)\n\n__Note:__ To coerce a list of key-value pairs like `['name', 'Ironman', 'victories', 2000]` to an object, use the [object](http://rethinkdb.com/api/python/object) command.\n\n*Example* Coerce a number to a string.\n\n r.expr(1).coerce_to('string').run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.default, b'value.default(default_value) -> any\nsequence.default(default_value) -> any\n\nHandle non-existence errors. Tries to evaluate and return its first argument. If an\nerror related to the absence of a value is thrown in the process, or if its first\nargument returns `None`, returns its second argument. (Alternatively, the second argument\nmay be a function which will be called with either the text of the non-existence error\nor `None`.)\n\n*Example* Suppose we want to retrieve the titles and authors of the table `posts`.\nIn the case where the author field is missing or `None`, we want to retrieve the string\n`Anonymous`.\n\n r.table("posts").map(lambda post:\n {\n "title": post["title"],\n "author": post["author"].default("Anonymous")\n }\n ).run(conn)\n\nWe can rewrite the previous query with `r.branch` too.\n\n r.table("posts").map(lambda post:\n r.branch(\n post.has_fields("author"),\n {\n "title": post["title"],\n "author": post["author"]\n },\n {\n "title": post["title"],\n "author": "Anonymous" \n }\n )\n ).run(conn)\n\n*Example* The `default` command can be useful to filter documents too. Suppose\nwe want to retrieve all our users who are not grown-ups or whose age is unknown\n(i.e the field `age` is missing or equals `None`). We can do it with this query:\n\n r.table("users").filter(lambda user:\n (user["age"] < 18).default(True)\n ).run(conn)\n\nOne more way to write the previous query is to set the age to be `-1` when the\nfield is missing.\n\n r.table("users").filter(lambda user:\n user["age"].default(-1) < 18\n ).run(conn)\n\nOne last way to do the same query is to use `has_fields`.\n\n r.table("users").filter(lambda user:\n user.has_fields("age").not_() | (user["age"] < 18)\n ).run(conn)\n\nThe body of every `filter` is wrapped in an implicit `.default(False)`. You can overwrite\nthe value `False` by passing an option in filter, so the previous query can also be\nwritten like this.\n\n r.table("users").filter(\n lambda user: (user["age"] < 18).default(True),\n default=True\n ).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.do, b"any.do(function) -> any\nr.do([args]*, function) -> any\nany.do(expr) -> any\nr.do([args]*, expr) -> any\n\nCall an anonymous function using return values from other ReQL commands or queries as arguments.\n\nThe last argument to `do` (or, in some forms, the only argument) is an expression or an anonymous function which receives values from either the previous arguments or from prefixed commands chained before `do`. The `do` command is essentially a single-element [map](http://rethinkdb.com/api/python/map/), letting you map a function over just one document. This allows you to bind a query result to a local variable within the scope of `do`, letting you compute the result just once and reuse it in a complex expression or in a series of ReQL commands.\n\nArguments passed to the `do` function must be basic data types, and cannot be streams or selections. (Read about [ReQL data types](http://rethinkdb.com/docs/data-types/).) While the arguments will all be evaluated before the function is executed, they may be evaluated in any order, so their values should not be dependent on one another. The type of `do`'s result is the type of the value returned from the function or last expression.\n\n*Example* Compute a golfer's net score for a game.\n\n r.table('players').get('86be93eb-a112-48f5-a829-15b2cb49de1d').do(\n lambda player: player['gross_score'] - player['course_handicap']\n ).run(conn)\n\n*Example* Return the name of the best scoring player in a two-player golf match.\n\n r.do(r.table('players').get(id1), r.table('players').get(id2),\n (lambda player1, player2:\n r.branch(player1['gross_score'].lt(player2['gross_score']),\n player1, player2))\n ).run(conn)\n\nNote that `branch`, the ReQL conditional command, must be used instead of `if`. See the `branch` [documentation](http://rethinkdb.com/api/python/branch) for more.\n\n*Example* Take different actions based on the result of a ReQL [insert](http://rethinkdb.com/api/python/insert) command.\n\n new_data = {\n 'id': 100,\n 'name': 'Agatha',\n 'gross_score': 57,\n 'course_handicap': 4\n }\n r.table('players').insert(new_data).do(lambda doc:\n r.branch((doc['inserted'] != 0),\n r.table('log').insert({'time': r.now(), 'response': doc, 'result': 'ok'}),\n r.table('log').insert({'time': r.now(), 'response': doc, 'result': 'error'}))\n ).run(conn)\n"),
+ (rethinkdb.error, b"r.error(message) -> error\n\nThrow a runtime error. If called with no arguments inside the second argument to `default`, re-throw the current error.\n\n*Example* Iron Man can't possibly have lost a battle:\n\n r.table('marvel').get('IronMan').do(\n lambda ironman: r.branch(ironman['victories'] < ironman['battles'],\n r.error('impossible code path'),\n ironman)\n ).run(conn)\n\n"),
+ (rethinkdb.expr, b"r.expr(value) -> value\n\nConstruct a ReQL JSON object from a native object.\n\nIf the native object is of the `bytes` type, then `expr` will return a binary object. See [binary](http://rethinkdb.com/api/python/binary) for more information.\n\n*Example* Objects wrapped with expr can then be manipulated by ReQL API functions.\n\n r.expr({'a':'b'}).merge({'b':[1,2,3]}).run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.for_each, b"sequence.for_each(write_query) -> object\n\nLoop over a sequence, evaluating the given write query for each element.\n\n*Example* Now that our heroes have defeated their villains, we can safely remove them from the villain table.\n\n r.table('marvel').for_each(\n lambda hero: r.table('villains').get(hero['villainDefeated']).delete()\n ).run(conn)\n\n"),
+ (rethinkdb.http, b'r.http(url[, options]) -> value\nr.http(url[, options]) -> stream\n\nRetrieve data from the specified URL over HTTP. The return type depends on the `result_format` option, which checks the `Content-Type` of the response by default.\n\n*Example* Perform an HTTP `GET` and store the result in a table.\n\n r.table(\'posts\').insert(r.http(\'http://httpbin.org/get\')).run(conn)\n\nSee [the tutorial](http://rethinkdb.com/docs/external-api-access/) on `r.http` for more examples on how to use this command.\n\n* `timeout`: Number of seconds to wait before timing out and aborting the operation. Default: 30.\n\n* `reattempts`: An integer giving the number of attempts to make in cast of connection errors or potentially-temporary HTTP errors. Default: 5.\n\n* `redirects`: An integer giving the number of redirects and location headers to follow. Default: 1.\n\n* `verify`: Verify the server\'s SSL certificate, specified as a boolean. Default: True.\n\n* `result_format`: The format the result should be returned in. The values can be `\'text\'` (always return as a string), `\'json\'` (parse the result as JSON, raising an error if the parsing fails), `\'jsonp\'` (parse the result as [padded JSON](http://www.json-p.org/)), `\'binary\'` (return a binary object), or `\'auto\'` . The default is `\'auto\'`.\n\n When `result_format` is `\'auto\'`, the response body will be parsed according to the `Content-Type` of the response:\n * `application/json`: parse as `\'json\'`\n * `application/json-p`, `text/json-p`, `text/javascript`: parse as `\'jsonp\'`\n * `audio/*`, `video/*`, `image/*`, `application/octet-stream`: return a binary object\n * Anything else: parse as `\'text\'`\n\n* `method`: HTTP method to use for the request. One of `GET`, `POST`, `PUT`, `PATCH`, `DELETE` or `HEAD`. Default: `GET`.\n\n* `auth`: Authentication information in the form of an object with key/value pairs indicating the authentication type (in the `type` key) and any required information. Types currently supported are `basic` and `digest` for HTTP Basic and HTTP Digest authentication respectively. If `type` is omitted, `basic` is assumed. Example:\n\n\t```py\n\tr.http(\'http://httpbin.org/basic-auth/fred/mxyzptlk\',\n auth={ \'type\': \'basic\', \'user\': \'fred\', \'pass\': \'mxyzptlk\' }).run(conn)\n\t```\n\n* `params`: URL parameters to append to the URL as encoded key/value pairs, specified as an object. For example, `{ \'query\': \'banana\', \'limit\': 2 }` will be appended as `?query=banana&limit=2`. Default: none.\n\n* `header`: Extra header lines to include. The value may be an array of strings or an object. Default: none.\n\n Unless specified otherwise, `r.http` will by default use the headers `Accept-Encoding: deflate=1;gzip=0.5` and `User-Agent: RethinkDB/VERSION`.\n\n* `data`: Data to send to the server on a `POST`, `PUT`, `PATCH`, or `DELETE` request.\n\n For `PUT`, `PATCH` and `DELETE` requests, the value will be serialized to JSON and placed in the request body, and the `Content-Type` will be set to `application/json`.\n\n\tFor `POST` requests, data may be either an object or a string. Objects will be written to the body as form-encoded key/value pairs (values must be numbers, strings, or `None`). Strings will be put directly into the body. If `data` is not a string or an object, an error will be thrown.\n\n If `data` is not specified, no data will be sent.\n\n`r.http` supports depagination, which will request multiple pages in a row and aggregate the results into a stream. The use of this feature is controlled by the optional arguments `page` and `page_limit`. Either none or both of these arguments must be provided.\n\n* `page`: This option may specify either a built-in pagination strategy (as a string), or a function to provide the next URL and/or `params` to request.\n\n At the moment, the only supported built-in is `\'link-next\'`, which is equivalent to `lambda info: info[\'header\'][\'link\'][\'rel="next"\'].default(None)`.\n\n *Example* Perform a GitHub search and collect up to 3 pages of results.\n\n ```py\n r.http("https://api.github.com/search/code?q=addClass+user:mozilla",\n page=\'link-next\', page_limit=3).run(conn)\n ```\n\n As a function, `page` takes one parameter, an object of the format:\n\n ```py\n {\n \'params\': object, # the URL parameters used in the last request\n \'header\': object, # the HTTP headers of the last response as key/value pairs\n \'body\': value # the body of the last response in the format specified by `result_format`\n }\n ```\n\n The `header` field will be a parsed version of the header with fields lowercased, like so:\n\n ```py\n {\n \'content-length\': \'1024\',\n \'content-type\': \'application/json\',\n \'date\': \'Thu, 1 Jan 1970 00:00:00 GMT\',\n \'link\': {\n \'rel="last"\': \'http://example.com/?page=34\',\n \'rel="next"\': \'http://example.com/?page=2\'\n }\n }\n ```\n\n The `page` function may return a string corresponding to the next URL to request, `None` indicating that there is no more to get, or an object of the format:\n\n ```py\n {\n \'url\': string, # the next URL to request, or None for no more pages\n \'params\': object # new URL parameters to use, will be merged with the previous request\'s params\n }\n ```\n\n* `page_limit`: An integer specifying the maximum number of requests to issue using the `page` functionality. This is to prevent overuse of API quotas, and must be specified with `page`.\n * `-1`: no limit\n * `0`: no requests will be made, an empty stream will be returned\n * `n`: `n` requests will be made\n\n# Examples\n\n*Example* Perform multiple requests with different parameters.\n\n r.expr([1, 2, 3]).map(lambda i: r.http(\'http://httpbin.org/get\',\n params={ \'user\': i })).run(conn)\n\n*Example* Perform a `PUT` request for each item in a table.\n\n r.table(\'data\').map(lambda row: r.http(\'http://httpbin.org/put\',\n method=\'PUT\', data=row)).run(conn)\n\n*Example* Perform a `POST` request with accompanying data.\n\nUsing form-encoded data:\n\n r.http(\'http://httpbin.org/post\',\n method=\'POST\',\n data={ \'player\': \'Bob\', \'game\': \'tic tac toe\' }).run(conn)\n\nUsing JSON data:\n\n r.http(\'http://httpbin.org/post\',\n method=\'POST\',\n data=r.expr(value).coerce_to(\'string\'),\n header={ \'Content-Type\': \'application/json\' }).run(conn)\n\n*Example* Perform depagination with a custom `page` function.\n\n r.http(\'example.com/pages\',\n page=lambda info: info[\'body\'][\'meta\'][\'next\'].default(None),\n page_limit=5).run(conn)\n\n# Learn more\n\nSee [the tutorial](http://rethinkdb.com/docs/external-api-access/) on `r.http` for more examples on how to use this command.\n'),
+ (rethinkdb.ast.RqlQuery.info, b"any.info() -> object\n\nGet information about a ReQL value.\n\n*Example* Get information about a table such as primary key, or cache size.\n\n r.table('marvel').info().run(conn)\n\n"),
+ (rethinkdb.js, b'r.js(js_string[, timeout=<number>]) -> value\n\nCreate a javascript expression.\n\n*Example* Concatenate two strings using JavaScript.\n\n`timeout` is the number of seconds before `r.js` times out. The default value is 5 seconds.\n\n{% infobox %}\nWhenever possible, you should use native ReQL commands rather than `r.js` for better performance.\n{% endinfobox %}\n\n r.js("\'str1\' + \'str2\'").run(conn)\n\n*Example* Select all documents where the \'magazines\' field is greater than 5 by running JavaScript on the server.\n\n r.table(\'marvel\').filter(\n r.js(\'(function (row) { return row.magazines.length > 5; })\')\n ).run(conn)\n\n*Example* You may also specify a timeout in seconds (defaults to 5).\n\n r.js(\'while(true) {}\', timeout=1.3).run(conn)\n\n'),
+ (rethinkdb.json, b'r.json(json_string) -> value\n\nParse a JSON string on the server.\n\n*Example* Send an array to the server\'\n\n r.json("[1,2,3]").run(conn)\n\n'),
+ (rethinkdb.range, b'r.range() -> stream\nr.range([start_value, ]end_value) -> stream\n\nGenerate a stream of sequential integers in a specified range. `range` takes 0, 1 or 2 arguments:\n\n* With no arguments, `range` returns an "infinite" stream from 0 up to and including the maximum integer value;\n* With one argument, `range` returns a stream from 0 up to but not including the end value;\n* With two arguments, `range` returns a stream from the start value up to but not including the end value.\n\nNote that the left bound (including the implied left bound of 0 in the 0- and 1-argument form) is always closed and the right bound is always open: the start value will always be included in the returned range and the end value will *not* be included in the returned range.\n\nAny specified arguments must be integers, or a `RqlRuntimeError` will be thrown. If the start value is equal or to higher than the end value, no error will be thrown but a zero-element stream will be returned.\n\n*Example* Return a four-element range of `[0, 1, 2, 3]`.\n\n > r.range(4).run(conn)\n \n [0, 1, 2, 3]\n\nYou can also use the [limit](http://rethinkdb.com/api/python/limit) command with the no-argument variant to achieve the same result in this case:\n\n > r.range().limit(4).run(conn)\n \n [0, 1, 2, 3]\n\n*Example* Return a range from -5 through 5.\n\n > r.range(-5, 6).run(conn)\n \n [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]\n'),
+ (rethinkdb.ast.RqlQuery.to_json_string, b'value.to_json_string() -> string\nvalue.to_json() -> string\n\nConvert a ReQL value or object to a JSON string. You may use either `to_json_string` or `to_json`.\n\n*Example* Get a ReQL document as a JSON string.\n\n > r.table(\'hero\').get(1).to_json()\n \n \'{"id": 1, "name": "Batman", "city": "Gotham", "powers": ["martial arts", "cinematic entrances"]}\'\n'),
+ (rethinkdb.ast.RqlQuery.to_json, b'value.to_json_string() -> string\nvalue.to_json() -> string\n\nConvert a ReQL value or object to a JSON string. You may use either `to_json_string` or `to_json`.\n\n*Example* Get a ReQL document as a JSON string.\n\n > r.table(\'hero\').get(1).to_json()\n \n \'{"id": 1, "name": "Batman", "city": "Gotham", "powers": ["martial arts", "cinematic entrances"]}\'\n'),
+ (rethinkdb.ast.RqlQuery.type_of, b'any.type_of() -> string\n\nGets the type of a value.\n\n*Example* Get the type of a string.\n\n r.expr("foo").type_of().run(conn)\n\n'),
+ (rethinkdb.uuid, b'r.uuid() -> string\n\nReturn a UUID (universally unique identifier), a string that can be used as a unique ID.\n\n*Example* Generate a UUID.\n\n > r.uuid().run(conn)\n \n 27961a0e-f4e8-4eb3-bf95-c5203e1d87b9\n'),
+ (rethinkdb.net.Cursor.close, b'cursor.close()\n\nClose a cursor. Closing a cursor cancels the corresponding query and frees the memory\nassociated with the open request.\n\n*Example* Close a cursor.\n\n cursor.close()\n'),
+ (rethinkdb.net.Cursor.next, b"cursor.next([wait=True])\n\nGet the next element in the cursor.\n\nThe optional `wait` argument specifies whether to wait for the next available element and how long to wait:\n\n* `True`: Wait indefinitely (the default).\n* `False`: Do not wait at all. If data is immediately available, it will be returned; if it is not available, a `RqlDriverError` will be raised.\n* number: Wait up the specified number of seconds for data to be available before raising `RqlDriverError`.\n\nThe behavior of `next` will be identical with `False`, `None` or the number `0`.\n\nCalling `next` the first time on a cursor provides the first element of the cursor. If the data set is exhausted (e.g., you have retrieved all the documents in a table), a `StopIteration` error will be raised when `next` is called.\n\n*Example* Retrieve the next element.\n\n cursor = r.table('superheroes').run(conn)\n doc = cursor.next()\n\n*Example* Retrieve the next element on a [changefeed](http://rethinkdb.com/docs/changefeeds/python), waiting up to five seconds.\n\n cursor = r.table('superheroes').changes().run(conn)\n doc = cursor.next(wait=5)\n\n__Note:__ RethinkDB sequences can be iterated through via the Python [Iterable][it] interface. The canonical way to retrieve all the results is to use a [for...in](../each/) loop or [list()](../to_array/).\n\n[it]: https://docs.python.org/3.4/library/stdtypes.html#iterator-types\n"),
+ (rethinkdb.ast.RqlQuery.date, b'time.date() -> time\n\nReturn a new time object only based on the day, month and year (ie. the same day at 00:00).\n\n*Example* Retrieve all the users whose birthday is today\n\n r.table("users").filter(lambda user:\n user["birthdate"].date() == r.now().date()\n ).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.day, b'time.day() -> number\n\nReturn the day of a time object as a number between 1 and 31.\n\n*Example* Return the users born on the 24th of any month.\n\n r.table("users").filter(\n r.row["birthdate"].day() == 24\n )\n\n'),
+ (rethinkdb.ast.RqlQuery.day_of_week, b'time.day_of_week() -> number\n\nReturn the day of week of a time object as a number between 1 and 7 (following ISO 8601 standard). For your convenience, the terms r.monday, r.tuesday etc. are defined and map to the appropriate integer.\n\n*Example* Return today\'s day of week.\n\n r.now().day_of_week().run(conn)\n\n*Example* Retrieve all the users who were born on a Tuesday.\n\n r.table("users").filter( lambda user:\n user["birthdate"].day_of_week().eq(r.tuesday)\n )\n\n'),
+ (rethinkdb.ast.RqlQuery.day_of_year, b'time.day_of_year() -> number\n\nReturn the day of the year of a time object as a number between 1 and 366 (following ISO 8601 standard).\n\n*Example* Retrieve all the users who were born the first day of a year.\n\n r.table("users").filter(\n r.row["birthdate"].day_of_year() == 1\n ).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.during, b'time.during(start_time, end_time[, left_bound="closed", right_bound="open"])\n -> bool\n\nReturn whether a time is between two other times. By default, this is inclusive of the start time and exclusive of the end time. Set `left_bound` and `right_bound` to explicitly include (`closed`) or exclude (`open`) that endpoint of the range.\n\n*Example* Retrieve all the posts that were posted between December 1st, 2013 (inclusive) and December 10th, 2013 (exclusive).\n\n r.table("posts").filter(\n r.row[\'date\'].during(r.time(2013, 12, 1, "Z"), r.time(2013, 12, 10, "Z"))\n ).run(conn)\n\n*Example* Retrieve all the posts that were posted between December 1st, 2013 (exclusive) and December 10th, 2013 (inclusive).\n\n r.table("posts").filter(\n r.row[\'date\'].during(r.time(2013, 12, 1, "Z"), r.time(2013, 12, 10, "Z"), left_bound="open", right_bound="closed")\n ).run(conn)\n\n'),
+ (rethinkdb.epoch_time, b'r.epoch_time(epoch_time) -> time\n\nCreate a time object based on seconds since epoch. The first argument is a double and\nwill be rounded to three decimal places (millisecond-precision).\n\n*Example* Update the birthdate of the user "John" to November 3rd, 1986.\n\n r.table("user").get("John").update({"birthdate": r.epoch_time(531360000)}).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.hours, b'time.hours() -> number\n\nReturn the hour in a time object as a number between 0 and 23.\n\n*Example* Return all the posts submitted after midnight and before 4am.\n\n r.table("posts").filter(lambda post:\n post["date"].hours() < 4\n ).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.in_timezone, b"time.in_timezone(timezone) -> time\n\nReturn a new time object with a different timezone. While the time stays the same, the results returned by methods such as hours() will change since they take the timezone into account. The timezone argument has to be of the ISO 8601 format.\n\n*Example* Hour of the day in San Francisco (UTC/GMT -8, without daylight saving time).\n\n r.now().in_timezone('-08:00').hours().run(conn)\n"),
+ (rethinkdb.iso8601, b'r.iso8601(iso8601Date[, default_timezone=\'\']) -> time\n\nCreate a time object based on an ISO 8601 date-time string (e.g. \'2013-01-01T01:01:01+00:00\'). We support all valid ISO 8601 formats except for week dates. If you pass an ISO 8601 date-time without a time zone, you must specify the time zone with the `default_timezone` argument. Read more about the ISO 8601 format at [Wikipedia](http://en.wikipedia.org/wiki/ISO_8601).\n\n*Example* Update the time of John\'s birth.\n\n r.table("user").get("John").update({"birth": r.iso8601(\'1986-11-03T08:30:00-07:00\')}).run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.minutes, b'time.minutes() -> number\n\nReturn the minute in a time object as a number between 0 and 59.\n\n*Example* Return all the posts submitted during the first 10 minutes of every hour.\n\n r.table("posts").filter(lambda post:\n post["date"].minutes() < 10\n ).run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.month, b'time.month() -> number\n\nReturn the month of a time object as a number between 1 and 12. For your convenience, the terms r.january, r.february etc. are defined and map to the appropriate integer.\n\n*Example* Retrieve all the users who were born in November.\n\n r.table("users").filter(\n r.row["birthdate"].month() == 11\n )\n\n*Example* Retrieve all the users who were born in November.\n\n r.table("users").filter(\n r.row["birthdate"].month() == r.november\n )\n\n'),
+ (rethinkdb.now, b'r.now() -> time\n\nReturn a time object representing the current time in UTC. The command now() is computed once when the server receives the query, so multiple instances of r.now() will always return the same time inside a query.\n\n*Example* Add a new user with the time at which he subscribed.\n\n r.table("users").insert({\n "name": "John",\n "subscription_date": r.now()\n }).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.seconds, b'time.seconds() -> number\n\nReturn the seconds in a time object as a number between 0 and 59.999 (double precision).\n\n*Example* Return the post submitted during the first 30 seconds of every minute.\n\n r.table("posts").filter(lambda post:\n post["date"].seconds() < 30\n ).run(conn)\n\n'),
+ (rethinkdb.time, b'r.time(year, month, day[, hour, minute, second], timezone)\n -> time\n\nCreate a time object for a specific time.\n\nA few restrictions exist on the arguments:\n\n- `year` is an integer between 1400 and 9,999.\n- `month` is an integer between 1 and 12.\n- `day` is an integer between 1 and 31.\n- `hour` is an integer.\n- `minutes` is an integer.\n- `seconds` is a double. Its value will be rounded to three decimal places\n(millisecond-precision).\n- `timezone` can be `\'Z\'` (for UTC) or a string with the format `\xc2\xb1[hh]:[mm]`.\n\n*Example* Update the birthdate of the user "John" to November 3rd, 1986 UTC.\n\n r.table("user").get("John").update({"birthdate": r.time(1986, 11, 3, \'Z\')}).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.time_of_day, b'time.time_of_day() -> number\n\nReturn the number of seconds elapsed since the beginning of the day stored in the time object.\n\n*Example* Retrieve posts that were submitted before noon.\n\n r.table("posts").filter(\n r.row["date"].time_of_day() <= 12*60*60\n ).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.timezone, b'time.timezone() -> string\n\nReturn the timezone of the time object.\n\n*Example* Return all the users in the "-07:00" timezone.\n\n r.table("users").filter(lambda user:\n user["subscriptionDate"].timezone() == "-07:00"\n )\n\n'),
+ (rethinkdb.ast.RqlQuery.to_epoch_time, b'time.to_epoch_time() -> number\n\nConvert a time object to its epoch time.\n\n*Example* Return the current time in seconds since the Unix Epoch with millisecond-precision.\n\n r.now().to_epoch_time()\n\n'),
+ (rethinkdb.ast.RqlQuery.to_iso8601, b'time.to_iso8601() -> string\n\nConvert a time object to a string in ISO 8601 format.\n\n*Example* Return the current ISO 8601 time.\n\n > r.now().to_iso8601().run(conn)\n \n "2015-04-20T18:37:52.690+00:00"\n\n'),
+ (rethinkdb.ast.RqlQuery.year, b'time.year() -> number\n\nReturn the year of a time object.\n\n*Example* Retrieve all the users born in 1986.\n\n r.table("users").filter(lambda user:\n user["birthdate"].year() == 1986\n ).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.append, b"array.append(value) -> array\n\nAppend a value to an array.\n\n*Example* Retrieve Iron Man's equipment list with the addition of some new boots.\n\n r.table('marvel').get('IronMan')['equipment'].append('newBoots').run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.__getitem__, b"sequence[attr] -> sequence\nsingleSelection[attr] -> value\nobject[attr] -> value\narray[index] -> value\n\nGet a single field from an object. If called on a sequence, gets that field from every object in the sequence, skipping objects that lack it.\n\n*Example* What was Iron Man's first appearance in a comic?\n\n r.table('marvel').get('IronMan')['firstAppearance'].run(conn)\n\nThe `[]` command also accepts integer arguments as array offsets, like the [nth](http://rethinkdb.com/api/python/nth) command.\n\n*Example* Get the fourth element in a sequence. (The first element is position `0`, so the fourth element is position `3`.)\n\n r.expr([10, 20, 30, 40, 50])[3]\n \n 40\n"),
+ (rethinkdb.ast.RqlQuery.change_at, b'array.change_at(index, value) -> array\n\nChange a value in an array at a given index. Returns the modified array.\n\n*Example* Bruce Banner hulks out.\n\n r.expr(["Iron Man", "Bruce", "Spider-Man"]).change_at(1, "Hulk").run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.delete_at, b"array.delete_at(index [,endIndex]) -> array\n\nRemove one or more elements from an array at a given index. Returns the modified array. (Note: `delete_at` operates on arrays, not documents; to delete documents, see the [delete](http://rethinkdb.com/api/python/delete) command.)\n\nIf only `index` is specified, `delete_at` removes the element at that index. If both `index` and `end_index` are specified, `delete_at` removes the range of elements between `index` and `end_index`, inclusive of `index` but not inclusive of `end_index`.\n\nIf `end_index` is specified, it must not be less than `index`. Both `index` and `end_index` must be within the array's bounds (i.e., if the array has 10 elements, an `index` or `end_index` of 10 or higher is invalid).\n\nBy using a negative `index` you can delete from the end of the array. `-1` is the last element in the array, `-2` is the second-to-last element, and so on. You may specify a negative `end_index`, although just as with a positive value, this will not be inclusive. The range `(2,-1)` specifies the third element through the next-to-last element.\n\n*Example* Delete the second element of an array.\n\n > r.expr(['a','b','c','d','e','f']).delete_at(1).run(conn)\n \n ['a', 'c', 'd', 'e', 'f']\n\n*Example* Delete the second and third elements of an array.\n\n > r.expr(['a','b','c','d','e','f']).delete_at(1,3).run(conn)\n \n ['a', 'd', 'e', 'f']\n\n*Example* Delete the next-to-last element of an array.\n\n > r.expr(['a','b','c','d','e','f']).delete_at(-2).run(conn)\n \n ['a', 'b', 'c', 'd', 'f']\n\n*Example* Delete a comment on a post.\n\nGiven a post document such as:\n\n{\n id: '4cf47834-b6f9-438f-9dec-74087e84eb63',\n title: 'Post title',\n author: 'Bob',\n comments: [\n { author: 'Agatha', text: 'Comment 1' },\n { author: 'Fred', text: 'Comment 2' }\n ]\n}\n\nThe second comment can be deleted by using `update` and `delete_at` together.\n\n r.table('posts').get('4cf47834-b6f9-438f-9dec-74087e84eb63').update(\n lambda post: { 'comments': post['comments'].delete_at(1) }\n ).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.difference, b"array.difference(array) -> array\n\nRemove the elements of one array from another array.\n\n*Example* Retrieve Iron Man's equipment list without boots.\n\n r.table('marvel').get('IronMan')['equipment'].difference(['Boots']).run(conn)\n\n*Example* Remove Iron Man's boots from his equipment.\n\n r.table('marvel').get('IronMan')[:equipment].update(lambda doc:\n {'equipment': doc['equipment'].difference(['Boots'])}\n ).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.get_field, b"sequence.get_field(attr) -> sequence\nsingleSelection.get_field(attr) -> value\nobject.get_field(attr) -> value\n\nGet a single field from an object. If called on a sequence, gets that field from every\nobject in the sequence, skipping objects that lack it.\n\n*Example* What was Iron Man's first appearance in a comic?\n\n r.table('marvel').get('IronMan').get_field('firstAppearance').run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.has_fields, b'sequence.has_fields([selector1, selector2...]) -> stream\narray.has_fields([selector1, selector2...]) -> array\nobject.has_fields([selector1, selector2...]) -> boolean\n\nTest if an object has one or more fields. An object has a field if it has that key and the key has a non-null value. For instance, the object `{\'a\': 1,\'b\': 2,\'c\': null}` has the fields `a` and `b`.\n\nWhen applied to a single object, `has_fields` returns `true` if the object has the fields and `false` if it does not. When applied to a sequence, it will return a new sequence (an array or stream) containing the elements that have the specified fields.\n\n*Example* Return the players who have won games.\n\n r.table(\'players\').has_fields(\'games_won\').run(conn)\n\n*Example* Return the players who have *not* won games. To do this, use `has_fields` with [not](http://rethinkdb.com/api/python/not), wrapped with [filter](http://rethinkdb.com/api/python/filter).\n\n r.table(\'players\').filter(~r.row.has_fields(\'games_won\')).run(conn)\n\n*Example* Test if a specific player has won any games.\n\n r.table(\'players\').get(\n \'b5ec9714-837e-400c-aa74-dbd35c9a7c4c\').has_fields(\'games_won\').run(conn)\n\n**Nested Fields**\n\n`has_fields` lets you test for nested fields in objects. If the value of a field is itself a set of key/value pairs, you can test for the presence of specific keys.\n\n*Example* In the `players` table, the `games_won` field contains one or more fields for kinds of games won:\n\n {\n \'games_won\': {\n \'playoffs\': 2,\n \'championships\': 1\n }\n }\n\nReturn players who have the "championships" field.\n\n r.table(\'players\').has_fields({\'games_won\': {\'championships\': true}}).run(conn)\n\nNote that `true` in the example above is testing for the existence of `championships` as a field, not testing to see if the value of the `championships` field is set to `true`. There\'s a more convenient shorthand form available. (See [pluck](http://rethinkdb.com/api/python/pluck) for more details on this.)\n\n r.table(\'players\').has_fields({\'games_won\': \'championships\'}).run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.insert_at, b'array.insert_at(index, value) -> array\n\nInsert a value in to an array at a given index. Returns the modified array.\n\n*Example* Hulk decides to join the avengers.\n\n r.expr(["Iron Man", "Spider-Man"]).insert_at(1, "Hulk").run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.keys, b"singleSelection.keys() -> array\nobject.keys() -> array\n\nReturn an array containing all of the object's keys.\n\n*Example* Get all the keys of a row.\n\n r.table('marvel').get('ironman').keys().run(conn)\n\n"),
+ (rethinkdb.literal, b'r.literal(object) -> special\n\nReplace an object in a field instead of merging it with an existing object in a `merge` or `update` operation. = Using `literal` with no arguments in a `merge` or `update` operation will remove the corresponding field.\n\n*Example* Replace one nested document with another rather than merging the fields.\n\nAssume your users table has this structure:\n\n [\n {\n "id": 1,\n "name": "Alice",\n "data": {\n "age": 18,\n "city": "Dallas"\n }\n } \n ...\n ]\n\nUsing `update` to modify the `data` field will normally merge the nested documents:\n\n r.table(\'users\').get(1).update({ \'data\': { \'age\': 19, \'job\': \'Engineer\' } }).run(conn)\n \n {\n "id": 1,\n "name": "Alice",\n "data": {\n "age": 19,\n "city": "Dallas",\n "job": "Engineer"\n }\n } \n\nThat will preserve `city` and other existing fields. But to replace the entire `data` document with a new object, use `literal`:\n\n r.table(\'users\').get(1).update({ \'data\': r.literal({ \'age\': 19, \'job\': \'Engineer\' }) }).run(conn)\n \n {\n "id": 1,\n "name": "Alice",\n "data": {\n "age": 19,\n "job": "Engineer"\n }\n } \n\n*Example* Use `literal` to remove a field from a document.\n\n r.table(\'users\').get(1).merge({ "data": r.literal() }).run(conn)\n \n {\n "id": 1,\n "name": "Alice"\n }\n'),
+ (rethinkdb.ast.RqlQuery.merge, b'singleSelection.merge(object|function[, object|function, ...]) -> object\nobject.merge(object|function[, object|function, ...]) -> object\nsequence.merge(object|function[, object|function, ...]) -> stream\narray.merge(object|function[, object|function, ...]) -> array\n\nMerge two or more objects together to construct a new object with properties from all. When there is a conflict between field names, preference is given to fields in the rightmost object in the argument list `merge` also accepts a subquery function that returns an object, which will be used similarly to a [map](http://rethinkdb.com/api/python/map/) function.\n\n*Example* Equip Thor for battle.\n\n r.table(\'marvel\').get(\'thor\').merge(\n r.table(\'equipment\').get(\'hammer\'),\n r.table(\'equipment\').get(\'pimento_sandwich\')\n ).run(conn)\n\n*Example* Equip every hero for battle, using a subquery function to retrieve their weapons.\n\n r.table(\'marvel\').merge(lambda hero:\n { \'weapons\': r.table(\'weapons\').get(hero[\'weapon_id\']) }\n ).run(conn)\n\n*Example* Use `merge` to join each blog post with its comments.\n\nNote that the sequence being merged&mdash;in this example, the comments&mdash;must be coerced from a selection to an array. Without `coerce_to` the operation will throw an error ("Expected type DATUM but found SELECTION").\n\n r.table(\'posts\').merge(lambda post:\n { \'comments\': r.table(\'comments\').get_all(post[\'id\'],\n index=\'post_id\').coerce_to(\'array\') }\n ).run(conn)\n\n*Example* Merge can be used recursively to modify object within objects.\n\n r.expr({\'weapons\' : {\'spectacular graviton beam\' : {\'dmg\' : 10, \'cooldown\' : 20}}}).merge(\n {\'weapons\' : {\'spectacular graviton beam\' : {\'dmg\' : 10}}}\n ).run(conn)\n\n*Example* To replace a nested object with another object you can use the literal keyword.\n\n r.expr({\'weapons\' : {\'spectacular graviton beam\' : {\'dmg\' : 10, \'cooldown\' : 20}}}).merge(\n {\'weapons\' : r.literal({\'repulsor rays\' : {\'dmg\' : 3, \'cooldown\' : 0}})}\n ).run(conn)\n\n*Example* Literal can be used to remove keys from an object as well.\n\n r.expr({\'weapons\' : {\'spectacular graviton beam\' : {\'dmg\' : 10, \'cooldown\' : 20}}}).merge(\n {\'weapons\' : {\'spectacular graviton beam\' : r.literal()}}\n ).run(conn)\n\n'),
+ (rethinkdb.object, b'r.object([key, value,]...) -> object\n\nCreates an object from a list of key-value pairs, where the keys must\nbe strings. `r.object(A, B, C, D)` is equivalent to\n`r.expr([[A, B], [C, D]]).coerce_to(\'OBJECT\')`.\n\n*Example* Create a simple object.\n\n > r.object(\'id\', 5, \'data\', [\'foo\', \'bar\']).run(conn)\n {\'data\': ["foo", "bar"], \'id\': 5}\n'),
+ (rethinkdb.ast.RqlQuery.pluck, b"sequence.pluck([selector1, selector2...]) -> stream\narray.pluck([selector1, selector2...]) -> array\nobject.pluck([selector1, selector2...]) -> object\nsingleSelection.pluck([selector1, selector2...]) -> object\n\nPlucks out one or more attributes from either an object or a sequence of objects\n(projection).\n\n*Example* We just need information about IronMan's reactor and not the rest of the\ndocument.\n\n r.table('marvel').get('IronMan').pluck('reactorState', 'reactorPower').run(conn)\n\n*Example* For the hero beauty contest we only care about certain qualities.\n\n r.table('marvel').pluck('beauty', 'muscleTone', 'charm').run(conn)\n\n*Example* Pluck can also be used on nested objects.\n\n r.table('marvel').pluck({'abilities' : {'damage' : True, 'mana_cost' : True}, 'weapons' : True}).run(conn)\n\n*Example* The nested syntax can quickly become overly verbose so there's a shorthand\nfor it.\n\n r.table('marvel').pluck({'abilities' : ['damage', 'mana_cost']}, 'weapons').run(conn)\n\nFor more information read the [nested field documentation](http://rethinkdb.com/docs/nested-fields/).\n"),
+ (rethinkdb.ast.RqlQuery.prepend, b"array.prepend(value) -> array\n\nPrepend a value to an array.\n\n*Example* Retrieve Iron Man's equipment list with the addition of some new boots.\n\n r.table('marvel').get('IronMan')['equipment'].prepend('newBoots').run(conn)\n"),
+ (rethinkdb.row, b"r.row -> value\n\nReturns the currently visited document. Note that `row` does not work within subqueries to access nested documents; you should use anonymous functions to access those documents instead. (See the last example.)\n\n*Example* Get all users whose age is greater than 5.\n\n r.table('users').filter(r.row['age'] > 5).run(conn)\n\n*Example* Access the attribute 'child' of an embedded document.\n\n r.table('users').filter(r.row['embedded_doc']['child'] > 5).run(conn)\n\n*Example* Add 1 to every element of an array.\n\n r.expr([1, 2, 3]).map(r.row + 1).run(conn)\n\n*Example* For nested queries, use functions instead of `row`.\n\n r.table('users').filter(\n lambda doc: doc['name'] == r.table('prizes').get('winner')\n ).run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.set_difference, b"array.set_difference(array) -> array\n\nRemove the elements of one array from another and return them as a set (an array with\ndistinct values).\n\n*Example* Check which pieces of equipment Iron Man has, excluding a fixed list.\n\n r.table('marvel').get('IronMan')['equipment'].set_difference(['newBoots', 'arc_reactor']).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.set_insert, b"array.set_insert(value) -> array\n\nAdd a value to an array and return it as a set (an array with distinct values).\n\n*Example* Retrieve Iron Man's equipment list with the addition of some new boots.\n\n r.table('marvel').get('IronMan')['equipment'].set_insert('newBoots').run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.set_intersection, b"array.set_intersection(array) -> array\n\nIntersect two arrays returning values that occur in both of them as a set (an array with\ndistinct values).\n\n*Example* Check which pieces of equipment Iron Man has from a fixed list.\n\n r.table('marvel').get('IronMan')['equipment'].set_intersection(['newBoots', 'arc_reactor']).run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.set_union, b"array.set_union(array) -> array\n\nAdd a several values to an array and return it as a set (an array with distinct values).\n\n*Example* Retrieve Iron Man's equipment list with the addition of some new boots and an arc reactor.\n\n r.table('marvel').get('IronMan')['equipment'].set_union(['newBoots', 'arc_reactor']).run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.splice_at, b'array.splice_at(index, array) -> array\n\nInsert several values in to an array at a given index. Returns the modified array.\n\n*Example* Hulk and Thor decide to join the avengers.\n\n r.expr(["Iron Man", "Spider-Man"]).splice_at(1, ["Hulk", "Thor"]).run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.without, b"sequence.without([selector1, selector2...]) -> stream\narray.without([selector1, selector2...]) -> array\nsingleSelection.without([selector1, selector2...]) -> object\nobject.without([selector1, selector2...]) -> object\n\nThe opposite of pluck; takes an object or a sequence of objects, and returns them with\nthe specified paths removed.\n\n*Example* Since we don't need it for this computation we'll save bandwidth and leave\nout the list of IronMan's romantic conquests.\n\n r.table('marvel').get('IronMan').without('personalVictoriesList').run(conn)\n\n*Example* Without their prized weapons, our enemies will quickly be vanquished.\n\n r.table('enemies').without('weapons').run(conn)\n\n*Example* Nested objects can be used to remove the damage subfield from the weapons and abilities fields.\n\n r.table('marvel').without({'weapons' : {'damage' : True}, 'abilities' : {'damage' : True}}).run(conn)\n\n*Example* The nested syntax can quickly become overly verbose so there's a shorthand for it.\n\n r.table('marvel').without({'weapons' : 'damage', 'abilities' : 'damage'}).run(conn)\n\n"),
+ (rethinkdb.circle, b"r.circle([longitude, latitude], radius[, num_vertices=32, geo_system='WGS84', unit='m', fill=True]) -> geometry\nr.circle(point, radius[, {num_vertices=32, geo_system='WGS84', unit='m', fill=True]) -> geometry\n\nConstruct a circular line or polygon. A circle in RethinkDB is a polygon or line *approximating* a circle of a given radius around a given center, consisting of a specified number of vertices (default 32).\n\nThe center may be specified either by two floating point numbers, the latitude (&minus;90 to 90) and longitude (&minus;180 to 180) of the point on a perfect sphere (see [Geospatial support](http://rethinkdb.com/docs/geo-support/) for more information on ReQL's coordinate system), or by a point object. The radius is a floating point number whose units are meters by default, although that may be changed with the `unit` argument.\n\nOptional arguments available with `circle` are:\n\n* `num_vertices`: the number of vertices in the polygon or line. Defaults to 32.\n* `geo_system`: the reference ellipsoid to use for geographic coordinates. Possible values are `WGS84` (the default), a common standard for Earth's geometry, or `unit_sphere`, a perfect sphere of 1 meter radius.\n* `unit`: Unit for the radius distance. Possible values are `m` (meter, the default), `km` (kilometer), `mi` (international mile), `nm` (nautical mile), `ft` (international foot).\n* `fill`: if `True` (the default) the circle is filled, creating a polygon; if `False` the circle is unfilled (creating a line).\n\n*Example* Define a circle.\n\n r.table('geo').insert({\n 'id': 300,\n 'name': 'Hayes Valley',\n 'neighborhood': r.circle([-122.423246,37.779388], 1000)\n }).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.distance, b"geometry.distance(geometry[, geo_system='WGS84', unit='m']) -> number\n\nCompute the distance between a point and another geometry object. At least one of the geometry objects specified must be a point.\n\nOptional arguments available with `distance` are:\n\n* `geo_system`: the reference ellipsoid to use for geographic coordinates. Possible values are `WGS84` (the default), a common standard for Earth's geometry, or `unit_sphere`, a perfect sphere of 1 meter radius.\n* `unit`: Unit to return the distance in. Possible values are `m` (meter, the default), `km` (kilometer), `mi` (international mile), `nm` (nautical mile), `ft` (international foot).\n\nIf one of the objects is a polygon or a line, the point will be projected onto the line or polygon assuming a perfect sphere model before the distance is computed (using the model specified with `geo_system`). As a consequence, if the polygon or line is extremely large compared to Earth's radius and the distance is being computed with the default WGS84 model, the results of `distance` should be considered approximate due to the deviation between the ellipsoid and spherical models.\n\n*Example* Compute the distance between two points on the Earth in kilometers.\n\n > point1 = r.point(-122.423246,37.779388)\n > point2 = r.point(-117.220406,32.719464)\n > r.distance(point1, point2, unit='km').run(conn)\n \n 734.1252496021841\n"),
+ (rethinkdb.ast.RqlQuery.fill, b"line.fill() -> polygon\n\nConvert a Line object into a Polygon object. If the last point does not specify the same coordinates as the first point, `polygon` will close the polygon by connecting them.\n\nLongitude (&minus;180 to 180) and latitude (&minus;90 to 90) of vertices are plotted on a perfect sphere. See [Geospatial support](http://rethinkdb.com/docs/geo-support/) for more information on ReQL's coordinate system.\n\nIf the last point does not specify the same coordinates as the first point, `polygon` will close the polygon by connecting them. You cannot directly construct a polygon with holes in it using `polygon`, but you can use [polygon_sub](http://rethinkdb.com/api/python/polygon_sub) to use a second polygon within the interior of the first to define a hole.\n\n*Example* Create a line object and then convert it to a polygon.\n\n r.table('geo').insert({\n 'id': 201,\n 'rectangle': r.line(\n [-122.423246,37.779388],\n [-122.423246,37.329898],\n [-121.886420,37.329898],\n [-121.886420,37.779388]\n )\n }).run(conn)\n \n r.table('geo').get(201).update({\n 'rectangle': r.row('rectangle').fill()\n }).run(conn)\n"),
+ (rethinkdb.geojson, b"r.geojson(geojson) -> geometry\n\nConvert a [GeoJSON][] object to a ReQL geometry object.\n\n[GeoJSON]: http://geojson.org\n\nRethinkDB only allows conversion of GeoJSON objects which have ReQL equivalents: Point, LineString, and Polygon. MultiPoint, MultiLineString, and MultiPolygon are not supported. (You could, however, store multiple points, lines and polygons in an array and use a geospatial multi index with them.)\n\nOnly longitude/latitude coordinates are supported. GeoJSON objects that use Cartesian coordinates, specify an altitude, or specify their own coordinate reference system will be rejected.\n\n*Example* Convert a GeoJSON object to a ReQL geometry object.\n\n geo_json = {\n 'type': 'Point',\n 'coordinates': [ -122.423246, 37.779388 ]\n }\n r.table('geo').insert({\n 'id': 'sfo',\n 'name': 'San Francisco',\n 'location': r.geojson(geo_json)\n }).run(conn)\n"),
+ (rethinkdb.ast.Table.get_intersecting, b"table.get_intersecting(geometry, index='indexname') -> selection<stream>\n\nGet all documents where the given geometry object intersects the geometry object of the requested geospatial index.\n\nThe `index` argument is mandatory. This command returns the same results as `table.filter(r.row('index').intersects(geometry))`. The total number of results is limited to the array size limit which defaults to 100,000, but can be changed with the `array_limit` option to [run](http://rethinkdb.com/api/python/run).\n\n*Example* Which of the locations in a list of parks intersect `circle1`?\n\n circle1 = r.circle([-117.220406,32.719464], 10, unit='mi')\n r.table('parks').get_intersecting(circle1, index='area').run(conn)\n"),
+ (rethinkdb.ast.Table.get_nearest, b"table.get_nearest(point, index='indexname'[, max_results=100, max_dist=100000, unit='m', geo_system='WGS84']) -> array\n\nGet all documents where the specified geospatial index is within a certain distance of the specified point (default 100 kilometers).\n\nThe `index` argument is mandatory. Optional arguments are:\n\n* `max_results`: the maximum number of results to return (default 100).\n* `unit`: Unit for the distance. Possible values are `m` (meter, the default), `km` (kilometer), `mi` (international mile), `nm` (nautical mile), `ft` (international foot).\n* `max_dist`: the maximum distance from an object to the specified point (default 100 km).\n* `geo_system`: the reference ellipsoid to use for geographic coordinates. Possible values are `WGS84` (the default), a common standard for Earth's geometry, or `unit_sphere`, a perfect sphere of 1 meter radius.\n\nThe return value will be an array of two-item objects with the keys `dist` and `doc`, set to the distance between the specified point and the document (in the units specified with `unit`, defaulting to meters) and the document itself, respectively.\n\n*Example* Return a list of enemy hideouts within 5000 meters of the secret base.\n\n secret_base = r.point(-122.422876,37.777128)\n r.table('hideouts').get_nearest(secret_base, index='location',\n max_dist=5000).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.includes, b"sequence.includes(geometry) -> sequence\ngeometry.includes(geometry) -> bool\n\nTests whether a geometry object is completely contained within another. When applied to a sequence of geometry objects, `includes` acts as a [filter](http://rethinkdb.com/api/python/filter), returning a sequence of objects from the sequence that include the argument.\n\n*Example* Is `point2` included within a 2000-meter circle around `point1`?\n\n > point1 = r.point(-117.220406,32.719464)\n > point2 = r.point(-117.206201,32.725186)\n > r.circle(point1, 2000).includes(point2).run(conn)\n \n True\n\n*Example* Which of the locations in a list of parks include `circle1`?\n\n circle1 = r.circle([-117.220406,32.719464], 10, unit='mi')\n r.table('parks')['area'].includes(circle1).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.intersects, b"sequence.intersects(geometry) -> sequence\ngeometry.intersects(geometry) -> bool\n\nTests whether two geometry objects intersect with one another. When applied to a sequence of geometry objects, `intersects` acts as a [filter](http://rethinkdb.com/api/python/filter), returning a sequence of objects from the sequence that intersect with the argument.\n\n*Example* Is `point2` within a 2000-meter circle around `point1`?\n\n > point1 = r.point(-117.220406,32.719464)\n > point2 = r.point(-117.206201,32.725186)\n > r.circle(point1, 2000).intersects(point2).run(conn)\n \n True\n\n*Example* Which of the locations in a list of parks intersect `circle1`?\n\n circle1 = r.circle([-117.220406,32.719464], 10, unit='mi')\n r.table('parks')('area').intersects(circle1).run(conn)\n"),
+ (rethinkdb.line, b"r.line([lon1, lat1], [lon2, lat2], ...) -> line\nr.line(point1, point2, ...) -> line\n\nConstruct a geometry object of type Line. The line can be specified in one of two ways:\n\n* Two or more two-item arrays, specifying latitude and longitude numbers of the line's vertices;\n* Two or more [Point](http://rethinkdb.com/api/python/point) objects specifying the line's vertices.\n\nLongitude (&minus;180 to 180) and latitude (&minus;90 to 90) of vertices are plotted on a perfect sphere. See [Geospatial support](http://rethinkdb.com/docs/geo-support/) for more information on ReQL's coordinate system.\n\n*Example* Define a line.\n\n r.table('geo').insert({\n 'id': 101,\n 'route': r.line([-122.423246,37.779388], [-121.886420,37.329898])\n }).run(conn)\n"),
+ (rethinkdb.point, b"r.point(longitude, latitude) -> point\n\nConstruct a geometry object of type Point. The point is specified by two floating point numbers, the longitude (&minus;180 to 180) and latitude (&minus;90 to 90) of the point on a perfect sphere. See [Geospatial support](http://rethinkdb.com/docs/geo-support/) for more information on ReQL's coordinate system.\n\n*Example* Define a point.\n\n r.table('geo').insert({\n 'id': 1,\n 'name': 'San Francisco',\n 'location': r.point(-122.423246,37.779388)\n }).run(conn)\n"),
+ (rethinkdb.polygon, b"r.polygon([lon1, lat1], [lon2, lat2], ...) -> polygon\nr.polygon(point1, point2, ...) -> polygon\n\nConstruct a geometry object of type Polygon. The Polygon can be specified in one of two ways:\n\n* Three or more two-item arrays, specifying latitude and longitude numbers of the polygon's vertices;\n* Three or more [Point](http://rethinkdb.com/api/python/point) objects specifying the polygon's vertices.\n\nLongitude (&minus;180 to 180) and latitude (&minus;90 to 90) of vertices are plotted on a perfect sphere. See [Geospatial support](http://rethinkdb.com/docs/geo-support/) for more information on ReQL's coordinate system.\n\nIf the last point does not specify the same coordinates as the first point, `polygon` will close the polygon by connecting them. You cannot directly construct a polygon with holes in it using `polygon`, but you can use [polygon_sub](http://rethinkdb.com/api/python/polygon_sub) to use a second polygon within the interior of the first to define a hole.\n\n*Example* Define a polygon.\n\n r.table('geo').insert({\n 'id': 101,\n 'rectangle': r.polygon(\n [-122.423246,37.779388],\n [-122.423246,37.329898],\n [-121.886420,37.329898],\n [-121.886420,37.779388]\n )\n }).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.polygon_sub, b'polygon1.polygon_sub(polygon2) -> polygon\n\nUse `polygon2` to "punch out" a hole in `polygon1`. `polygon2` must be completely contained within `polygon1` and must have no holes itself (it must not be the output of `polygon_sub` itself).\n\n*Example* Define a polygon with a hole punched in it.\n\n outer_polygon = r.polygon(\n [-122.4,37.7],\n [-122.4,37.3],\n [-121.8,37.3],\n [-121.8,37.7]\n )\n inner_polygon = r.polygon(\n [-122.3,37.4],\n [-122.3,37.6],\n [-122.0,37.6],\n [-122.0,37.4]\n )\n outer_polygon.polygon_sub(inner_polygon).run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.to_geojson, b"geometry.to_geojson() -> object\n\nConvert a ReQL geometry object to a [GeoJSON][] object.\n\n[GeoJSON]: http://geojson.org\n\n*Example* Convert a ReQL geometry object to a GeoJSON object.\n\n > r.table(geo).get('sfo')['location'].to_geojson().run(conn)\n \n {\n 'type': 'Point',\n 'coordinates': [ -122.423246, 37.779388 ]\n }\n"),
+ (rethinkdb.ast.RqlQuery.eq_join, b'sequence.eq_join(left_field, right_table[, index=\'id\']) -> sequence\n\nJoin tables using a field on the left-hand sequence matching primary keys or secondary indexes on the right-hand table. `eq_join` is more efficient than other Re_qL join types, and operates much faster. Documents in the result set consist of pairs of left-hand and right-hand documents, matched when the field on the left-hand side exists and is non-null and an entry with that field\'s value exists in the specified index on the right-hand side.\n\nThe result set of `eq_join` is a stream or array of objects. Each object in the returned set will be an object of the form `{ left: <left-document>, right: <right-document> }`, where the values of `left` and `right` will be the joined documents. Use the <code><a href="/api/python/zip/">zip</a></code> command to merge the `left` and `right` fields together.\n\n**Example:** Match players with the games they\'ve played against one another.\n\nThe players table contains these documents:\n\n [\n { \'id\': 1, \'player\': \'George\', \'gameId\': 1 },\n { \'id\': 2, \'player\': \'Agatha\', \'gameId\': 3 },\n { \'id\': 3, \'player\': \'Fred\', \'gameId\': 2 },\n { \'id\': 4, \'player\': \'Marie\', \'gameId\': 2 },\n { \'id\': 5, \'player\': \'Earnest\', \'gameId\': 1 },\n { \'id\': 6, \'player\': \'Beth\', \'gameId\': 3 }\n ]\n\nThe games table contains these documents:\n\n [\n { \'id\': 1, \'field\': \'Little Delving\' },\n { \'id\': 2, \'field\': \'Rushock Bog\' },\n { \'id\': 3, \'field\': \'Bucklebury\' }\n ]\n\nJoin these tables using `game_id` on the player table and `id` on the games table:\n\n r.table(\'players\').eq_join(\'game_id\', r.table(\'games\')).run(conn)\n\nThis will return a result set such as the following:\n\n [\n {\n "left" : { "gameId" : 3, "id" : 2, "player" : "Agatha" },\n "right" : { "id" : 3, "field" : "Bucklebury" }\n },\n {\n "left" : { "gameId" : 2, "id" : 3, "player" : "Fred" },\n "right" : { "id" : 2, "field" : "Rushock Bog" }\n },\n ...\n ]\n\nWhat you likely want is the result of using `zip` with that. For clarity, we\'ll use `without` to drop the `id` field from the games table (it conflicts with the `id` field for the players and it\'s redundant anyway), and we\'ll order it by the games.\n\n r.table(\'players\').eq_join(\'game_id\', r.table(\'games\')).without({\'right\': "id"}).zip().order_by(\'game_id\').run(conn)\n \n [\n { "field": "Little Delving", "gameId": 1, "id": 5, "player": "Earnest" },\n { "field": "Little Delving", "gameId": 1, "id": 1, "player": "George" },\n { "field": "Rushock Bog", "gameId": 2, "id": 3, "player": "Fred" },\n { "field": "Rushock Bog", "gameId": 2, "id": 4, "player": "Marie" },\n { "field": "Bucklebury", "gameId": 3, "id": 6, "player": "Beth" },\n { "field": "Bucklebury", "gameId": 3, "id": 2, "player": "Agatha" }\n ]\n\nFor more information, see [Table joins in Rethink_dB](http://rethinkdb.com/docs/table-joins/).\n\n**Example:** Use a secondary index on the right table rather than the primary key. If players have a secondary index on their cities, we can get a list of arenas with players in the same area.\n\n r.table(\'arenas\').eq_join(\'city_id\', r.table(\'arenas\'), index=\'city_id\').run(conn)\n\n**Example:** Use a nested key as the join field. Suppose the documents in the players table were structured like this:\n\n { \'id\': 1, \'player\': \'George\', \'game\': {\'id\': 1} },\n { \'id\': 2, \'player\': \'Agatha\', \'game\': {\'id\': 3} },\n ...\n\nSimply specify the field using the `row` command instead of a string.\n\n r.table(\'players\').eq_join(r.row[\'game\'][\'id\'], r.table(\'games\')).without({\'right\': \'id\'}).zip().run(conn)\n \n [\n { "field": "Little Delving", "game": { "id": 1 }, "id": 5, "player": "Earnest" },\n { "field": "Little Delving", "game": { "id": 1 }, "id": 1, "player": "George" },\n ...\n ]\n\n**Example:** Use a function instead of a field to join on a more complicated expression. Suppose the players have lists of favorite games ranked in order in a field such as `"favorites": [3, 2, 1]`. Get a list of players and their top favorite:\n\n r.table(\'players3\').eq_join(\n lambda player: player[\'favorites\'].nth(0),\n r.table(\'games\')\n ).without([{\'left\': [\'favorites\', \'game_id\', \'id\']}, {\'right\': \'id\'}]).zip()\n\nResult:\n\n [\n \t{ "field": "Rushock Bog", "name": "Fred" },\n \t{ "field": "Little Delving", "name": "George" },\n \t...\n ]\n'),
+ (rethinkdb.ast.RqlQuery.inner_join, b"sequence.inner_join(other_sequence, predicate) -> stream\narray.inner_join(other_sequence, predicate) -> array\n\nReturns an inner join of two sequences. The returned sequence represents an intersection of the left-hand sequence and the right-hand sequence: each row of the left-hand sequence will be compared with each row of the right-hand sequence to find all pairs of rows which satisfy the predicate. Each matched pair of rows of both sequences are combined into a result row. In most cases, you will want to follow the join with [zip](http://rethinkdb.com/api/python/zip) to combine the left and right results.\n\nNote that `inner_join` is slower and much less efficient than using [eq_join](http://rethinkdb.com/api/python/eq_join/) or [concat_map](http://rethinkdb.com/api/python/concat_map/) with [get_all](http://rethinkdb.com/api/python/get_all/). You should avoid using `inner_join` in commands when possible.\n\n*Example* Return a list of all matchups between Marvel and DC heroes in which the DC hero could beat the Marvel hero in a fight.\n\n r.table('marvel').inner_join(r.table('dc'),\n lambda marvel_row, dc_row: marvel_row['strength'] < dc_row['strength']\n ).zip().run(conn)\n\n(Compare this to an [outer_join](http://rethinkdb.com/api/python/outer_join) with the same inputs and predicate, which would return a list of *all* Marvel heroes along with any DC heroes with a higher strength.)"),
+ (rethinkdb.ast.RqlQuery.outer_join, b"sequence.outer_join(other_sequence, predicate) -> stream\narray.outer_join(other_sequence, predicate) -> array\n\nReturns a left outer join of two sequences. The returned sequence represents a union of the left-hand sequence and the right-hand sequence: all documents in the left-hand sequence will be returned, each matched with a document in the right-hand sequence if one satisfies the predicate condition. In most cases, you will want to follow the join with [zip](http://rethinkdb.com/api/python/zip) to combine the left and right results.\n\nNote that `outer_join` is slower and much less efficient than using [concat_map](http://rethinkdb.com/api/python/concat_map/) with [get_all](http://rethinkdb.com/api/python/get_all). You should avoid using `outer_join` in commands when possible.\n\n*Example* Return a list of all Marvel heroes, paired with any DC heroes who could beat them in a fight.\n\n r.table('marvel').outer_join(r.table('dc'),\n lambda marvel_row, dc_row: marvel_row['strength'] < dc_row['strength']\n ).zip().run(conn)\n\n(Compare this to an [inner_join](http://rethinkdb.com/api/python/inner_join) with the same inputs and predicate, which would return a list only of the matchups in which the DC hero has the higher strength.)\n"),
+ (rethinkdb.ast.RqlQuery.zip, b"stream.zip() -> stream\narray.zip() -> array\n\nUsed to 'zip' up the result of a join by merging the 'right' fields into 'left' fields of each member of the sequence.\n\n*Example* 'zips up' the sequence by merging the left and right fields produced by a join.\n\n r.table('marvel').eq_join('main_dc_collaborator', r.table('dc')).zip().run(conn)\n"),
+ (rethinkdb.db_create, b'r.db_create(db_name) -> object\n\nCreate a database. A RethinkDB database is a collection of tables, similar to\nrelational databases.\n\nIf successful, the command returns an object with two fields:\n\n* `dbs_created`: always `1`.\n* `config_changes`: a list containing one object with two fields, `old_val` and `new_val`:\n * `old_val`: always `None`.\n * `new_val`: the database\'s new [config](http://rethinkdb.com/api/python/config) value.\n\nIf a database with the same name already exists, the command throws `RqlRuntimeError`.\n\nNote: Only alphanumeric characters and underscores are valid for the database name.\n\n*Example* Create a database named \'superheroes\'.\n\n r.db_create(\'superheroes\').run(conn)\n \n {\n "config_changes": [\n {\n "new_val": {\n "id": "e4689cfc-e903-4532-a0e6-2d6797a43f07",\n "name": "superheroes"\n },\n "old_val": None\n }\n ],\n "dbs_created": 1\n }\n\n'),
+ (rethinkdb.db_drop, b'r.db_drop(db_name) -> object\n\nDrop a database. The database, all its tables, and corresponding data will be deleted.\n\nIf successful, the command returns an object with two fields:\n\n* `dbs_dropped`: always `1`.\n* `tables_dropped`: the number of tables in the dropped database.\n* `config_changes`: a list containing one two-field object, `old_val` and `new_val`:\n * `old_val`: the database\'s original [config](http://rethinkdb.com/api/python/config) value.\n * `new_val`: always `None`.\n\nIf the given database does not exist, the command throws `RqlRuntimeError`.\n\n*Example* Drop a database named \'superheroes\'.\n\n r.db_drop(\'superheroes\').run(conn)\n \n {\n "config_changes": [\n {\n "old_val": {\n "id": "e4689cfc-e903-4532-a0e6-2d6797a43f07",\n "name": "superheroes"\n },\n "new_val": None\n }\n ],\n "tables_dropped": 3,\n "dbs_dropped": 1\n }\n\n'),
+ (rethinkdb.db_list, b'r.db_list() -> array\n\nList all database names in the system. The result is a list of strings.\n\n*Example* List all databases.\n\n r.db_list().run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.changes, b'stream.changes(squash=True, include_states=False) -> stream\nsingleSelection.changes(squash=True, include_states=False) -> stream\n\nReturn an infinite stream of objects representing changes to a query.\n\nThe `squash` optional argument controls how `changes` batches change notifications:\n\n* `True`: When multiple changes to the same document occur before a batch of notifications is sent, the changes are "squashed" into one change. The client receives a notification that will bring it fully up to date with the server. This is the default.\n* `False`: All changes will be sent to the client verbatim.\n* `n`: A numeric value (floating point). Similar to `True`, but the server will wait `n` seconds to respond in order to squash as many changes together as possible, reducing network traffic.\n\nIf the `include_states` optional argument is `True`, the changefeed stream will include special status documents consisting of the field `state` and a string indicating a change in the feed\'s state. These documents can occur at any point in the feed between the notification documents described below. There are currently two states:\n\n* `{"state": "initializing"}` indicates the following documents represent initial values on the feed rather than changes. This will be the first document of a feed that returns initial values.\n* `{"state": "ready"}` indicates the following documents represent changes. This will be the first document of a feed that does *not* return initial values; otherwise, it will indicate the initial values have all been sent.\n\nPoint changefeeds will always return initial values and have an `initializing` state; feeds that return changes on unfiltered tables will never return initial values. Feeds that return changes on more complex queries may or may not return return initial values, depending on the kind of aggregation. Read the article on [Changefeeds in RethinkDB][cfr] for a more detailed discussion. If `include_states` is `True` on a changefeed that does not return initial values, the first document on the feed will be `{"state": "ready"}`.\n\n[cfr]: /docs/changefeeds/python/\n\nIf `include_states` is `False` (the default), the status documents will not be sent on the feed.\n\nIf the table becomes unavailable, the changefeed will be disconnected, and a runtime exception will be thrown by the driver.\n\nChangefeed notifications take the form of a two-field object:\n\n {\n "old_val": <document before change>,\n "new_val": <document after change>\n }\n\nThe first notification object in the changefeed stream will contain the query\'s initial value in `new_val` and have no `old_val` field. When a document is deleted, `new_val` will be `None`; when a document is inserted, `old_val` will be `None`.\n\nCertain document transformation commands can be chained before changefeeds. For more information, read the [discussion of changefeeds][cfr] in the "Query language" documentation.\n\nThe server will buffer up to 100,000 elements. If the buffer limit is hit, early changes will be discarded, and the client will receive an object of the form `{"error": "Changefeed cache over array size limit, skipped X elements."}` where `X` is the number of elements skipped.\n\nCommands that operate on streams (such as `filter` or `map`) can usually be chained after `changes`. However, since the stream produced by `changes` has no ending, commands that need to consume the entire stream before returning (such as `reduce` or `count`) cannot.\n\nIt\'s a good idea to open changefeeds on their own connection. If you don\'t, other queries run on the same connection will experience unpredictable latency spikes while the connection blocks on more changes.\n\n*Example* Subscribe to the changes on a table.\n\nStart monitoring the changefeed in one client:\n\n for change in r.table(\'games\').changes().run(conn):\n print change\n\nAs these queries are performed in a second client, the first client would receive and print the following objects:\n\n > r.table(\'games\').insert({\'id\': 1}).run(conn)\n {\'old_val\': None, \'new_val\': {\'id\': 1}}\n \n > r.table(\'games\').get(1).update({\'player1\': \'Bob\'}).run(conn)\n {\'old_val\': {\'id\': 1}, \'new_val\': {\'id\': 1, \'player1\': \'Bob\'}}\n \n > r.table(\'games\').get(1).replace({\'id\': 1, \'player1\': \'Bob\', \'player2\': \'Alice\'}).run(conn)\n {\'old_val\': {\'id\': 1, \'player1\': \'Bob\'},\n \'new_val\': {\'id\': 1, \'player1\': \'Bob\', \'player2\': \'Alice\'}}\n \n > r.table(\'games\').get(1).delete().run(conn)\n {\'old_val\': {\'id\': 1, \'player1\': \'Bob\', \'player2\': \'Alice\'}, \'new_val\': None}\n \n > r.table_drop(\'games\').run(conn)\n RqlRuntimeError: Changefeed aborted (table unavailable)\n\n*Example* Return all the changes that increase a player\'s score.\n\n r.table(\'test\').changes().filter(\n r.row[\'new_val\'][\'score\'] > r.row[\'old_val\'][\'score\']\n ).run(conn)\n\n*Example* Return all the changes to Bob\'s score.\n\n # Note that this will have to look at and discard all the changes to\n # rows besides Bob\'s. This is currently no way to filter with an index\n # on changefeeds.\n r.table(\'test\').changes().filter(r.row[\'new_val\'][\'name\'].eq(\'Bob\')).run(conn)\n\n*Example* Return all the inserts on a table.\n\n r.table(\'test\').changes().filter(r.row[\'old_val\'].eq(None)).run(conn)\n\n*Example* Return all the changes to game 1, with state notifications.\n\n r.table(\'games\').get(1).changes(include_states=True).run(conn)\n \n # result returned on changefeed\n {"state": "initializing"}\n {"new_val": {"id": 1, "score": 12, "arena": "Hobbiton Field"}}\n {"state": "ready"}\n {\n \t"old_val": {"id": 1, "score": 12, "arena": "Hobbiton Field"},\n \t"new_val": {"id": 1, "score": 14, "arena": "Hobbiton Field"}\n }\n {\n \t"old_val": {"id": 1, "score": 14, "arena": "Hobbiton Field"},\n \t"new_val": {"id": 1, "score": 17, "arena": "Hobbiton Field", "winner": "Frodo"}\n }\n\n*Example* Return all the changes to the top 10 games. This assumes the presence of a `score` secondary index on the `games` table.\n\n r.table(\'games\').order_by(index=r.desc(\'score\')).limit(10).run(conn)\n'),
+ (rethinkdb.ast.Table.index_create, b'table.index_create(index_name[, index_function][, multi=False, geo=False]) -> object\n\nCreate a new secondary index on a table. Secondary indexes improve the speed of many read queries at the slight cost of increased storage space and decreased write performance. For more information about secondary indexes, read the article "[Using secondary indexes in RethinkDB](http://rethinkdb.com/docs/secondary-indexes/)."\n\nRethinkDB supports different types of secondary indexes:\n\n- *Simple indexes* based on the value of a single field.\n- *Compound indexes* based on multiple fields.\n- *Multi indexes* based on arrays of values.\n- *Geospatial indexes* based on indexes of geometry objects, created when the `geo` optional argument is true.\n- Indexes based on *arbitrary expressions*.\n\nThe `index_function` can be an anonymous function or a binary representation obtained from the `function` field of [index_status](http://rethinkdb.com/api/python/index_status).\n\nIf successful, `create_index` will return an object of the form `{"created": 1}`. If an index by that name already exists on the table, a `RqlRuntimeError` will be thrown.\n\n*Example* Create a simple index based on the field `post_id`.\n\n r.table(\'comments\').index_create(\'post_id\').run(conn)\n*Example* Create a simple index based on the nested field `author > name`.\n\n r.table(\'comments\').index_create(\'author_name\', r.row["author"]["name"]).run(conn)\n\n*Example* Create a geospatial index based on the field `location`.\n\n r.table(\'places\').index_create(\'location\', geo=True).run(conn)\n\nA geospatial index field should contain only geometry objects. It will work with geometry ReQL terms ([get_intersecting](http://rethinkdb.com/api/python/get_intersecting/) and [get_nearest](http://rethinkdb.com/api/python/get_nearest/)) as well as index-specific terms ([index_status](http://rethinkdb.com/api/python/index_status), [index_wait](http://rethinkdb.com/api/python/index_wait), [index_drop](http://rethinkdb.com/api/python/index_drop) and [index_list](http://rethinkdb.com/api/python/index_list)). Using terms that rely on non-geometric ordering such as [get_all](http://rethinkdb.com/api/python/get_all/), [order_by](http://rethinkdb.com/api/python/order_by/) and [between](http://rethinkdb.com/api/python/order_by/) will result in an error.\n\n*Example* Create a compound index based on the fields `post_id` and `date`.\n\n r.table(\'comments\').index_create(\'post_and_date\', [r.row["post_id"], r.row["date"]]).run(conn)\n\n*Example* Create a multi index based on the field `authors`.\n\n r.table(\'posts\').index_create(\'authors\', multi=True).run(conn)\n\n*Example* Create a geospatial multi index based on the field `towers`.\n\n r.table(\'networks\').index_create(\'towers\', geo=True, multi=True).run(conn)\n\n*Example* Create an index based on an arbitrary expression.\n\n r.table(\'posts\').index_create(\'authors\', lambda doc:\n r.branch(\n doc.has_fields("updated_at"),\n doc["updated_at"],\n doc["created_at"]\n )\n ).run(conn)\n\n*Example* Create a new secondary index based on an existing one.\n\n index = r.table(\'posts\').index_status(\'authors\').nth(0)[\'function\'].run(conn)\n r.table(\'new_posts\').index_create(\'authors\', index).run(conn)\n\n*Example* Rebuild an outdated secondary index on a table.\n\n old_index = r.table(\'posts\').index_status(\'old_index\').nth(0)[\'function\'].run(conn)\n r.table(\'posts\').index_create(\'new_index\', old_index).run(conn)\n r.table(\'posts\').index_wait(\'new_index\').run(conn)\n r.table(\'posts\').index_rename(\'new_index\', \'old_index\', overwrite=True).run(conn)\n'),
+ (rethinkdb.ast.Table.index_drop, b"table.index_drop(index_name) -> object\n\nDelete a previously created secondary index of this table.\n\n*Example* Drop a secondary index named 'code_name'.\n\n r.table('dc').index_drop('code_name').run(conn)\n\n"),
+ (rethinkdb.ast.Table.index_list, b"table.index_list() -> array\n\nList all the secondary indexes of this table.\n\n*Example* List the available secondary indexes for this table.\n\n r.table('marvel').index_list().run(conn)\n"),
+ (rethinkdb.ast.Table.index_rename, b"table.index_rename(old_index_name, new_index_name[, overwrite=False]) -> object\n\nRename an existing secondary index on a table. If the optional argument `overwrite` is specified as `True`, a previously existing index with the new name will be deleted and the index will be renamed. If `overwrite` is `False` (the default) an error will be raised if the new index name already exists.\n\nThe return value on success will be an object of the format `{'renamed': 1}`, or `{'renamed': 0}` if the old and new names are the same.\n\nAn error will be raised if the old index name does not exist, if the new index name is already in use and `overwrite` is `False`, or if either the old or new index name are the same as the primary key field name.\n\n*Example* Rename an index on the comments table.\n\n r.table('comments').index_rename('post_id', 'message_id').run(conn)\n"),
+ (rethinkdb.ast.Table.index_status, b'table.index_status([, index...]) -> array\n\nGet the status of the specified indexes on this table, or the status\nof all indexes on this table if no indexes are specified.\n\nThe result is an array where for each index, there will be an object like this one:\n\n {\n "index": <index_name>,\n "ready": True,\n "function": <binary>,\n "multi": <bool>,\n "outdated": <bool>\n }\n\nor this one:\n\n {\n "index": <index_name>,\n "ready": False,\n "blocks_processed": <int>,\n "blocks_total": <int>,\n "function": <binary>,\n "multi": <bool>,\n "outdated": <bool>\n }\n\nThe `multi` field will be `true` or `false` depending on whether this index was created as a multi index (see [index_create](http://rethinkdb.com/api/python/index_create/) for details). The `outdated` field will be true if the index is outdated in the current version of RethinkDB and needs to be rebuilt.\n\nThe `function` field is a binary object containing an opaque representation of the secondary index (including the `multi` argument if specified). It can be passed as the second argument to [index_create](http://rethinkdb.com/api/python/index_create/) to create a new index with the same function; see `index_create` for more information.\n\n*Example* Get the status of all the indexes on `test`:\n\n r.table(\'test\').index_status().run(conn)\n\n*Example* Get the status of the `timestamp` index:\n\n r.table(\'test\').index_status(\'timestamp\').run(conn)\n\n*Example* Save the binary representation of the index:\n\n func = r.table(\'test\').index_status(\'timestamp\').nth(0)[\'function\'].run(conn)\n'),
+ (rethinkdb.ast.Table.index_wait, b'table.index_wait([, index...]) -> array\n\nWait for the specified indexes on this table to be ready, or for all\nindexes on this table to be ready if no indexes are specified.\n\nThe result is an array containing one object for each table index:\n\n {\n "index": <index_name>,\n "ready": True,\n "function": <binary>,\n "multi": <bool>,\n "geo": <bool>,\n "outdated": <bool>\n }\n\nSee the [index_status](http://rethinkdb.com/api/python/index_status) documentation for a description of the field values.\n\n*Example* Wait for all indexes on the table `test` to be ready:\n\n r.table(\'test\').index_wait().run(conn)\n\n*Example* Wait for the index `timestamp` to be ready:\n\n r.table(\'test\').index_wait(\'timestamp\').run(conn)\n'),
+ (rethinkdb.ast.DB.table_create, b'db.table_create(table_name[, options]) -> object\n\nCreate a table. A RethinkDB table is a collection of JSON documents.\n\nIf successful, the command returns an object with two fields:\n\n* `tables_created`: always `1`.\n* `config_changes`: a list containing one two-field object, `old_val` and `new_val`:\n * `old_val`: always `None`.\n * `new_val`: the table\'s new [config](http://rethinkdb.com/api/python/config) value.\n\nIf a table with the same name already exists, the command throws `RqlRuntimeError`.\n\nNote: Only alphanumeric characters and underscores are valid for the table name.\n\nWhen creating a table you can specify the following options:\n\n* `primary_key`: the name of the primary key. The default primary key is `id`.\n* `durability`: if set to `soft`, writes will be acknowledged by the server immediately and flushed to disk in the background. The default is `hard`: acknowledgment of writes happens after data has been written to disk.\n* `shards`: the number of shards, an integer from 1-32. Defaults to `1`.\n* `replicas`: either an integer or a mapping object. Defaults to `1`.\n * If `replicas` is an integer, it specifies the number of replicas per shard. Specifying more replicas than there are servers will return an error.\n * If `replicas` is an object, it specifies key-value pairs of server tags and the number of replicas to assign to those servers: `{\'tag1\': 2, \'tag2\': 4, \'tag3\': 2, ...}`.\n* `primary_replica_tag`: the primary server specified by its server tag. Required if `replicas` is an object; the tag must be in the object. This must *not* be specified if `replicas` is an integer.\n\nThe [data type](http://rethinkdb.com/docs/data-types/) of a primary key is usually a string (like a UUID) or a number, but it can also be a time, binary object, boolean or an array. It cannot be an object.\n\n*Example* Create a table named \'dc_universe\' with the default settings.\n\n r.db(\'test\').table_create(\'dc_universe\').run(conn)\n \n {\n "config_changes": [\n {\n "new_val": {\n "db": "test",\n "durability": "hard",\n "id": "20ea60d4-3b76-4817-8828-98a236df0297",\n "name": "dc_universe",\n "primary_key": "id",\n "shards": [\n {\n "primary_replica": "rethinkdb_srv1",\n "replicas": [\n "rethinkdb_srv1",\n "rethinkdb_srv2"\n ]\n }\n ],\n "write_acks": "majority"\n },\n "old_val": None\n }\n ],\n "tables_created": 1\n }\n\n*Example* Create a table named \'dc_universe\' using the field \'name\' as primary key.\n\n r.db(\'test\').table_create(\'dc_universe\', primary_key=\'name\').run(conn)\n\n*Example* Create a table set up for two shards and three replicas per shard. This requires three available servers.\n\n r.db(\'test\').table_create(\'dc_universe\', shards=2, replicas=3).run(conn)\n\nRead [Sharding and replication](http://rethinkdb.com/docs/sharding-and-replication/) for a complete discussion of the subject, including advanced topics.\n'),
+ (rethinkdb.ast.DB.table_drop, b'db.table_drop(table_name) -> object\n\nDrop a table. The table and all its data will be deleted.\n\nIf successful, the command returns an object with two fields:\n\n* `tables_dropped`: always `1`.\n* `config_changes`: a list containing one two-field object, `old_val` and `new_val`:\n * `old_val`: the dropped table\'s [config](http://rethinkdb.com/api/python/config) value.\n * `new_val`: always `None`.\n\nIf the given table does not exist in the database, the command throws `RqlRuntimeError`.\n\n*Example* Drop a table named \'dc_universe\'.\n\n r.db(\'test\').table_drop(\'dc_universe\').run(conn)\n \n {\n "config_changes": [\n {\n "old_val": {\n "db": "test",\n "durability": "hard",\n "id": "20ea60d4-3b76-4817-8828-98a236df0297",\n "name": "dc_universe",\n "primary_key": "id",\n "shards": [\n {\n "primary_replica": "rethinkdb_srv1",\n "replicas": [\n "rethinkdb_srv1",\n "rethinkdb_srv2"\n ]\n }\n ],\n "write_acks": "majority"\n },\n "new_val": None\n }\n ],\n "tables_dropped": 1\n }\n'),
+ (rethinkdb.ast.DB.table_list, b"db.table_list() -> array\n\nList all table names in a database. The result is a list of strings.\n\n*Example* List all tables of the 'test' database.\n\n r.db('test').table_list().run(conn)\n \n"),
+ (rethinkdb.ast.RqlQuery.__add__, b'number + number -> number\nstring + string -> string\narray + array -> array\ntime + number -> time\n\nSum two numbers, concatenate two strings, or concatenate 2 arrays.\n\n*Example* It\'s as easy as 2 + 2 = 4.\n\n > (r.expr(2) + 2).run(conn)\n \n 4\n\n*Example* Strings can be concatenated too.\n\n > (r.expr("foo") + "bar").run(conn)\n \n "foobar"\n\n*Example* Arrays can be concatenated too.\n\n > (r.expr(["foo", "bar"]) + ["buzz"]).run(conn)\n \n [\'foo\', \'bar\', \'buzz\']\n\n*Example* Create a date one year from now.\n\n r.now() + 365*24*60*60\n\n*Example* Use [args](http://rethinkdb.com/api/python/args) with `add` to sum multiple values.\n\n > r.add(r.args([10, 20, 30])).run(conn)\n \n 60\n\n*Example* Concatenate an array of strings with `args`.\n\n > r.add(r.args([\'foo\', \'bar\', \'buzz\'])).run(conn)\n \n "foobarbuzz"\n'),
+ (rethinkdb.add, b'number + number -> number\nstring + string -> string\narray + array -> array\ntime + number -> time\n\nSum two numbers, concatenate two strings, or concatenate 2 arrays.\n\n*Example* It\'s as easy as 2 + 2 = 4.\n\n > (r.expr(2) + 2).run(conn)\n \n 4\n\n*Example* Strings can be concatenated too.\n\n > (r.expr("foo") + "bar").run(conn)\n \n "foobar"\n\n*Example* Arrays can be concatenated too.\n\n > (r.expr(["foo", "bar"]) + ["buzz"]).run(conn)\n \n [\'foo\', \'bar\', \'buzz\']\n\n*Example* Create a date one year from now.\n\n r.now() + 365*24*60*60\n\n*Example* Use [args](http://rethinkdb.com/api/python/args) with `add` to sum multiple values.\n\n > r.add(r.args([10, 20, 30])).run(conn)\n \n 60\n\n*Example* Concatenate an array of strings with `args`.\n\n > r.add(r.args([\'foo\', \'bar\', \'buzz\'])).run(conn)\n \n "foobarbuzz"\n'),
+ (rethinkdb.ast.RqlQuery.__and__, b'bool & bool -> bool\nr.and_(bool, bool) -> bool\nbool.and_(bool) -> bool\n\nCompute the logical "and" of two or more values. The `and_` command can be used as an infix operator after its first argument (`r.expr(True).and_(False)`) or given all of its arguments as parameters (`r.and_(True, False)`). The standard Python and operator, `&`, may also be used with ReQL.\n\n*Example* Return whether both `a` and `b` evaluate to true.\n\n > a = True\n > b = False\n > (r.expr(a) & b).run(conn)\n \n False\n*Example* Return whether all of `x`, `y` and `z` evaluate to true.\n\n > x = True\n > y = True\n > z = True\n > r.and_(x, y, z).run(conn)\n \n True\n'),
+ (rethinkdb.and_, b'bool & bool -> bool\nr.and_(bool, bool) -> bool\nbool.and_(bool) -> bool\n\nCompute the logical "and" of two or more values. The `and_` command can be used as an infix operator after its first argument (`r.expr(True).and_(False)`) or given all of its arguments as parameters (`r.and_(True, False)`). The standard Python and operator, `&`, may also be used with ReQL.\n\n*Example* Return whether both `a` and `b` evaluate to true.\n\n > a = True\n > b = False\n > (r.expr(a) & b).run(conn)\n \n False\n*Example* Return whether all of `x`, `y` and `z` evaluate to true.\n\n > x = True\n > y = True\n > z = True\n > r.and_(x, y, z).run(conn)\n \n True\n'),
+ (rethinkdb.ast.RqlQuery.__div__, b"number / number -> number\n\nDivide two numbers.\n\n*Example* It's as easy as 2 / 2 = 1.\n\n (r.expr(2) / 2).run(conn)\n"),
+ (rethinkdb.div, b"number / number -> number\n\nDivide two numbers.\n\n*Example* It's as easy as 2 / 2 = 1.\n\n (r.expr(2) / 2).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.__eq__, b'value == value -> bool\nvalue.eq(value) -> bool\n\nTest if two values are equal.\n\n*Example* Does 2 equal 2?\n\n (r.expr(2) == 2).run(conn)\n r.expr(2).eq(2).run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.eq, b'value == value -> bool\nvalue.eq(value) -> bool\n\nTest if two values are equal.\n\n*Example* Does 2 equal 2?\n\n (r.expr(2) == 2).run(conn)\n r.expr(2).eq(2).run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.__ge__, b'value >= value -> bool\nvalue.ge(value) -> bool\n\nTest if the first value is greater than or equal to other.\n\n*Example* Is 2 greater than or equal to 2?\n\n (r.expr(2) >= 2).run(conn)\n r.expr(2).ge(2).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.ge, b'value >= value -> bool\nvalue.ge(value) -> bool\n\nTest if the first value is greater than or equal to other.\n\n*Example* Is 2 greater than or equal to 2?\n\n (r.expr(2) >= 2).run(conn)\n r.expr(2).ge(2).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.__gt__, b'value > value -> bool\nvalue.gt(value) -> bool\n\nTest if the first value is greater than other.\n\n*Example* Is 2 greater than 2?\n\n (r.expr(2) > 2).run(conn)\n r.expr(2).gt(2).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.gt, b'value > value -> bool\nvalue.gt(value) -> bool\n\nTest if the first value is greater than other.\n\n*Example* Is 2 greater than 2?\n\n (r.expr(2) > 2).run(conn)\n r.expr(2).gt(2).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.__le__, b'value <= value -> bool\nvalue.le(value) -> bool\n\nTest if the first value is less than or equal to other.\n\n*Example* Is 2 less than or equal to 2?\n\n (r.expr(2) <= 2).run(conn)\n r.expr(2).le(2).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.le, b'value <= value -> bool\nvalue.le(value) -> bool\n\nTest if the first value is less than or equal to other.\n\n*Example* Is 2 less than or equal to 2?\n\n (r.expr(2) <= 2).run(conn)\n r.expr(2).le(2).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.__lt__, b'value < value -> bool\nvalue.lt(value) -> bool\n\nTest if the first value is less than other.\n\n*Example* Is 2 less than 2?\n\n (r.expr(2) < 2).run(conn)\n r.expr(2).lt(2).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.lt, b'value < value -> bool\nvalue.lt(value) -> bool\n\nTest if the first value is less than other.\n\n*Example* Is 2 less than 2?\n\n (r.expr(2) < 2).run(conn)\n r.expr(2).lt(2).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.__mod__, b"number % number -> number\n\nFind the remainder when dividing two numbers.\n\n*Example* It's as easy as 2 % 2 = 0.\n\n (r.expr(2) % 2).run(conn)\n\n`\n"),
+ (rethinkdb.mod, b"number % number -> number\n\nFind the remainder when dividing two numbers.\n\n*Example* It's as easy as 2 % 2 = 0.\n\n (r.expr(2) % 2).run(conn)\n\n`\n"),
+ (rethinkdb.ast.RqlQuery.__mul__, b'number * number -> number\narray * number -> array\n\nMultiply two numbers, or make a periodic array.\n\n*Example* It\'s as easy as 2 * 2 = 4.\n\n (r.expr(2) * 2).run(conn)\n\n*Example* Arrays can be multiplied by numbers as well.\n\n (r.expr(["This", "is", "the", "song", "that", "never", "ends."]) * 100).run(conn)\n\n'),
+ (rethinkdb.mul, b'number * number -> number\narray * number -> array\n\nMultiply two numbers, or make a periodic array.\n\n*Example* It\'s as easy as 2 * 2 = 4.\n\n (r.expr(2) * 2).run(conn)\n\n*Example* Arrays can be multiplied by numbers as well.\n\n (r.expr(["This", "is", "the", "song", "that", "never", "ends."]) * 100).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.__ne__, b'value != value -> bool\nvalue.ne(value) -> bool\n\nTest if two values are not equal.\n\n*Example* Does 2 not equal 2?\n\n (r.expr(2) != 2).run(conn)\n r.expr(2).ne(2).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.ne, b'value != value -> bool\nvalue.ne(value) -> bool\n\nTest if two values are not equal.\n\n*Example* Does 2 not equal 2?\n\n (r.expr(2) != 2).run(conn)\n r.expr(2).ne(2).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.__invert__, b'bool.not_() -> bool\nnot_(bool) -> bool\n(~bool) -> bool\n\nCompute the logical inverse (not) of an expression.\n\n`not_` can be called either via method chaining, immediately after an expression that evaluates as a boolean value, or by passing the expression as a parameter to `not_`. All values that are not `False` or `None` will be converted to `True`.\n\nYou may also use `~` as a shorthand operator.\n\n*Example* Not true is false.\n\n r.not_(True).run(conn)\n r.expr(True).not_().run(conn)\n (~r.expr(True)).run(conn)\n\nThese evaluate to `false`.\n\nNote that when using `~` the expression is wrapped in parentheses. Without this, Python will evaluate `r.expr(True)` *first* rather than using the ReQL operator and return an incorrect value. (`~True` evaluates to &minus;2 in Python.)\n\n*Example* Return all the users that do not have a "flag" field.\n\n r.table(\'users\').filter(\n lambda users: (~users.has_fields(\'flag\'))\n ).run(conn)\n\n*Example* As above, but prefix-style.\n\n r.table(\'users\').filter(\n lambda users: r.not_(users.has_fields(\'flag\'))\n ).run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.not_, b'bool.not_() -> bool\nnot_(bool) -> bool\n(~bool) -> bool\n\nCompute the logical inverse (not) of an expression.\n\n`not_` can be called either via method chaining, immediately after an expression that evaluates as a boolean value, or by passing the expression as a parameter to `not_`. All values that are not `False` or `None` will be converted to `True`.\n\nYou may also use `~` as a shorthand operator.\n\n*Example* Not true is false.\n\n r.not_(True).run(conn)\n r.expr(True).not_().run(conn)\n (~r.expr(True)).run(conn)\n\nThese evaluate to `false`.\n\nNote that when using `~` the expression is wrapped in parentheses. Without this, Python will evaluate `r.expr(True)` *first* rather than using the ReQL operator and return an incorrect value. (`~True` evaluates to &minus;2 in Python.)\n\n*Example* Return all the users that do not have a "flag" field.\n\n r.table(\'users\').filter(\n lambda users: (~users.has_fields(\'flag\'))\n ).run(conn)\n\n*Example* As above, but prefix-style.\n\n r.table(\'users\').filter(\n lambda users: r.not_(users.has_fields(\'flag\'))\n ).run(conn)\n'),
+ (rethinkdb.not_, b'bool.not_() -> bool\nnot_(bool) -> bool\n(~bool) -> bool\n\nCompute the logical inverse (not) of an expression.\n\n`not_` can be called either via method chaining, immediately after an expression that evaluates as a boolean value, or by passing the expression as a parameter to `not_`. All values that are not `False` or `None` will be converted to `True`.\n\nYou may also use `~` as a shorthand operator.\n\n*Example* Not true is false.\n\n r.not_(True).run(conn)\n r.expr(True).not_().run(conn)\n (~r.expr(True)).run(conn)\n\nThese evaluate to `false`.\n\nNote that when using `~` the expression is wrapped in parentheses. Without this, Python will evaluate `r.expr(True)` *first* rather than using the ReQL operator and return an incorrect value. (`~True` evaluates to &minus;2 in Python.)\n\n*Example* Return all the users that do not have a "flag" field.\n\n r.table(\'users\').filter(\n lambda users: (~users.has_fields(\'flag\'))\n ).run(conn)\n\n*Example* As above, but prefix-style.\n\n r.table(\'users\').filter(\n lambda users: r.not_(users.has_fields(\'flag\'))\n ).run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.__or__, b'bool | bool -> bool\nbool.or_(bool[, bool, ...]) -> bool\nr.or_(bool, bool) -> bool\n\nCompute the logical "or" of two or more values. The `or_` command can be used as an infix operator after its first argument (`r.expr(True).or_(False)`) or given all of its arguments as parameters (`r.or_(True, False)`). The standard Python or operator, `|`, may also be used with ReQL.\n\n*Example* Return whether either `a` or `b` evaluate to true.\n\n > a = True\n > b = False\n > (r.expr(a) | b).run(conn)\n \n True\n\n*Example* Return whether any of `x`, `y` or `z` evaluate to true.\n\n > x = False\n > y = False\n > z = False\n > r.or_(x, y, z).run(conn)\n \n False\n\n__Note:__ When using `or` inside a `filter` predicate to test the values of fields that may not exist on the documents being tested, you should use the `default` command with those fields so they explicitly return `False`.\n\n r.table(\'posts\').filter(lambda post:\n post[\'category\'].default(\'foo\').eq(\'article\').or(\n post[\'genre\'].default(\'foo\').eq(\'mystery\'))\n ).run(conn)\n'),
+ (rethinkdb.or_, b'bool | bool -> bool\nbool.or_(bool[, bool, ...]) -> bool\nr.or_(bool, bool) -> bool\n\nCompute the logical "or" of two or more values. The `or_` command can be used as an infix operator after its first argument (`r.expr(True).or_(False)`) or given all of its arguments as parameters (`r.or_(True, False)`). The standard Python or operator, `|`, may also be used with ReQL.\n\n*Example* Return whether either `a` or `b` evaluate to true.\n\n > a = True\n > b = False\n > (r.expr(a) | b).run(conn)\n \n True\n\n*Example* Return whether any of `x`, `y` or `z` evaluate to true.\n\n > x = False\n > y = False\n > z = False\n > r.or_(x, y, z).run(conn)\n \n False\n\n__Note:__ When using `or` inside a `filter` predicate to test the values of fields that may not exist on the documents being tested, you should use the `default` command with those fields so they explicitly return `False`.\n\n r.table(\'posts\').filter(lambda post:\n post[\'category\'].default(\'foo\').eq(\'article\').or(\n post[\'genre\'].default(\'foo\').eq(\'mystery\'))\n ).run(conn)\n'),
+ (rethinkdb.random, b"r.random() -> number\nr.random(number[, number], float=True) -> number\nr.random(integer[, integer]) -> integer\n\nGenerate a random number between given (or implied) bounds. `random` takes zero, one or two arguments.\n\n- With __zero__ arguments, the result will be a floating-point number in the range `[0,1)` (from 0 up to but not including 1).\n- With __one__ argument _x,_ the result will be in the range `[0,x)`, and will be integer unless `float=True` is given as an option. Specifying a floating point number without the `float` option will raise an error.\n- With __two__ arguments _x_ and _y,_ the result will be in the range `[x,y)`, and will be integer unless `float=True` is given as an option. If _x_ and _y_ are equal an error will occur, unless the floating-point option has been specified, in which case _x_ will be returned. Specifying a floating point number without the `float` option will raise an error.\n\nNote: The last argument given will always be the 'open' side of the range, but when\ngenerating a floating-point number, the 'open' side may be less than the 'closed' side.\n\n*Example* Generate a random number in the range `[0,1)`\n\n r.random().run(conn)\n\n*Example* Generate a random integer in the range `[0,100)`\n\n r.random(100).run(conn)\n r.random(0, 100).run(conn)\n\n*Example* Generate a random number in the range `(-2.24,1.59]`\n\n r.random(1.59, -2.24, float=True).run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.__sub__, b"number - number -> number\ntime - time -> number\ntime - number -> time\n\nSubtract two numbers.\n\n*Example* It's as easy as 2 - 2 = 0.\n\n (r.expr(2) - 2).run(conn)\n\n*Example* Create a date one year ago today.\n\n r.now() - 365*24*60*60\n\n*Example* Retrieve how many seconds elapsed between today and date\n\n r.now() - date\n\n"),
+ (rethinkdb.sub, b"number - number -> number\ntime - time -> number\ntime - number -> time\n\nSubtract two numbers.\n\n*Example* It's as easy as 2 - 2 = 0.\n\n (r.expr(2) - 2).run(conn)\n\n*Example* Create a date one year ago today.\n\n r.now() - 365*24*60*60\n\n*Example* Retrieve how many seconds elapsed between today and date\n\n r.now() - date\n\n"),
+ (rethinkdb.ast.Table.between, b'table.between(lower_key, upper_key[, index=\'id\', left_bound=\'closed\', right_bound=\'open\'])\n -> selection\n\nGet all documents between two keys. Accepts three optional arguments: `index`, `left_bound`, and `right_bound`. If `index` is set to the name of a secondary index, `between` will return all documents where that index\'s value is in the specified range (it uses the primary key by default). `left_bound` or `right_bound` may be set to `open` or `closed` to indicate whether or not to include that endpoint of the range (by default, `left_bound` is closed and `right_bound` is open).\n\nYou may also use the special constants `r.minval` and `r.maxval` for boundaries, which represent "less than any index key" and "more than any index key" respectively. For instance, if you use `r.minval` as the lower key, then `between` will return all documents whose primary keys (or indexes) are less than the specified upper key.\n\nNote that compound indexes are sorted using [lexicographical order][lo]. Take the following range as an example:\n\n\t[[1, "c"] ... [5, "e"]]\n\nThis range includes all compound keys:\n\n* whose first item is 1 and second item is equal or greater than "c";\n* whose first item is between 1 and 5, *regardless of the value of the second item*;\n* whose first item is 5 and second item is less than or equal to "e".\n\n[lo]: https://en.wikipedia.org/wiki/Lexicographical_order\n\n*Example* Find all users with primary key >= 10 and < 20 (a normal half-open interval).\n\n r.table(\'marvel\').between(10, 20).run(conn)\n\n*Example* Find all users with primary key >= 10 and <= 20 (an interval closed on both sides).\n\n r.table(\'marvel\').between(10, 20, right_bound=\'closed\').run(conn)\n\n*Example* Find all users with primary key < 20.\n\n r.table(\'marvel\').between(r.minval, 20).run(conn)\n\n*Example* Find all users with primary key > 10.\n\n r.table(\'marvel\').between(10, r.maxval, left_bound=\'open\').run(conn)\n\n*Example* Between can be used on secondary indexes too. Just pass an optional index argument giving the secondary index to query.\n\n r.table(\'dc\').between(\'dark_knight\', \'man_of_steel\', index=\'code_name\').run(conn)\n\n*Example* Get all users whose full name is between "John Smith" and "Wade Welles."\n\n r.table("users").between(["Smith", "John"], ["Welles", "Wade"],\n index="full_name").run(conn)\n\n*Example* Subscribe to a [changefeed](http://rethinkdb.com/docs/changefeeds/javascript) of teams ranked in the top 10.\n\n changes = r.table("teams").between(1, 11, index="rank").changes().run(conn)\n\n__Note:__ Between works with secondary indexes on date fields, but will not work with unindexed date fields. To test whether a date value is between two other dates, use the [during](http://rethinkdb.com/api/python/during) command, not `between`.\n\nSecondary indexes can be used in extremely powerful ways with `between` and other commands; read the full article on [secondary indexes](http://rethinkdb.com/docs/secondary-indexes) for examples using boolean operations, `contains` and more.\n\n__Note:__ RethinkDB uses byte-wise ordering for `between` and does not support Unicode collations; non-ASCII characters will be sorted by UTF-8 codepoint.\n\n__Note:__ If you chain `between` after [order_by](http://rethinkdb.com/api/python/order_by), the `between` command must use the index specified in `order_by`, and will default to that index. Trying to specify another index will result in a `RqlRuntimeError`.\n'),
+ (rethinkdb.db, b"r.db(db_name) -> db\n\nReference a database.\n\n*Example* Before we can query a table we have to select the correct database.\n\n r.db('heroes').table('marvel').run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.filter, b'selection.filter(predicate[, default=False]) -> selection\nstream.filter(predicate[, default=False]) -> stream\narray.filter(predicate[, default=False]) -> array\n\nReturn all the elements in a sequence for which the given predicate is true. The return value of `filter` will be the same as the input (sequence, stream, or array). Documents can be filtered in a variety of ways&mdash;ranges, nested values, boolean conditions, and the results of anonymous functions.\n\nBy default, `filter` will silently skip documents with missing fields: if the predicate tries to access a field that doesn\'t exist (for instance, the predicate `{\'age\': 30}` applied to a document with no `age` field), that document will not be returned in the result set, and no error will be generated. This behavior can be changed with the `default` optional argument.\n\n* If `default` is set to `True`, documents with missing fields will be returned rather than skipped.\n* If `default` is set to `r.error()`, an `RqlRuntimeError` will be thrown when a document with a missing field is tested.\n* If `default` is set to `False` (the default), documents with missing fields will be skipped.\n\n*Example* Get all users who are 30 years old.\n\n r.table(\'users\').filter({\'age\': 30}).run(conn)\n\nThe predicate `{\'age\': 30}` selects documents in the `users` table with an `age` field whose value is `30`. Documents with an `age` field set to any other value *or* with no `age` field present are skipped.\n\nWhile the `{\'field\': value}` style of predicate is useful for exact matches, a more general way to write a predicate is to use the [row](http://rethinkdb.com/api/python/row) command with a comparison operator such as [eq](http://rethinkdb.com/api/python/eq) (`==`) or [gt](http://rethinkdb.com/api/python/gt) (`>`), or to use a lambda function that returns `True` or `False`.\n\n r.table(\'users\').filter(r.row["age"] == 30).run(conn)\n\nIn this case, the predicate `r.row["age"] == 30` returns `True` if the field `age` is equal to 30. You can write this predicate as a lambda function instead:\n\n r.table(\'users\').filter(lambda user:\n user["age"] == 30\n ).run(conn)\n\nPredicates to `filter` are evaluated on the server, and must use ReQL expressions. Some Python comparison operators are overloaded by the RethinkDB driver and will be translated to ReQL, such as `==`, `<`/`>` and `|`/`&` (note the single character form, rather than `||`/`&&`).\n\nAlso, predicates must evaluate document fields. They cannot evaluate [secondary indexes](http://rethinkdb.com/docs/secondary-indexes/).\n\n*Example* Get all users who are more than 18 years old.\n\n r.table("users").filter(r.row["age"] > 18).run(conn)\n\n*Example* Get all users who are less than 18 years old and more than 13 years old.\n\n r.table("users").filter((r.row["age"] < 18) & (r.row["age"] > 13)).run(conn)\n\n*Example* Get all users who are more than 18 years old or have their parental consent.\n\n r.table("users").filter(\n (r.row["age"] >= 18) | (r.row["hasParentalConsent"])).run(conn)\n\n*Example* Retrieve all users who subscribed between January 1st, 2012\n(included) and January 1st, 2013 (excluded).\n\n r.table("users").filter(\n lambda user: user["subscription_date"].during(\n r.time(2012, 1, 1, \'Z\'), r.time(2013, 1, 1, \'Z\'))\n ).run(conn)\n\n*Example* Retrieve all users who have a gmail account (whose field `email` ends with `@gmail.com`).\n\n r.table("users").filter(\n lambda user: user["email"].match("@gmail.com$")\n ).run(conn)\n\n*Example* Filter based on the presence of a value in an array.\n\nGiven this schema for the `users` table:\n\n {\n "name": <type \'str\'>\n "places_visited": [<type \'str\'>]\n }\n\nRetrieve all users whose field `places_visited` contains `France`.\n\n r.table("users").filter(lambda user:\n user["places_visited"].contains("France")\n ).run(conn)\n\n*Example* Filter based on nested fields.\n\nGiven this schema for the `users` table:\n\n {\n "id": <type \'str\'>\n "name": {\n "first": <type \'str\'>,\n "middle": <type \'str\'>,\n "last": <type \'str\'>\n }\n }\n\nRetrieve all users named "William Adama" (first name "William", last name\n"Adama"), with any middle name.\n\n r.table("users").filter({\n "name": {\n "first": "William",\n "last": "Adama"\n }\n }).run(conn)\n\nIf you want an exact match for a field that is an object, you will have to use `r.literal`.\n\nRetrieve all users named "William Adama" (first name "William", last name\n"Adama"), and who do not have a middle name.\n\n r.table("users").filter(r.literal({\n "name": {\n "first": "William",\n "last": "Adama"\n }\n })).run(conn)\n\nYou may rewrite these with lambda functions.\n\n r.table("users").filter(\n lambda user:\n (user["name"]["first"] == "William")\n & (user["name"]["last"] == "Adama")\n ).run(conn)\n\n r.table("users").filter(lambda user:\n user["name"] == {\n "first": "William",\n "last": "Adama"\n }\n ).run(conn)\n\nBy default, documents missing fields tested by the `filter` predicate are skipped. In the previous examples, users without an `age` field are not returned. By passing the optional `default` argument to `filter`, you can change this behavior.\n\n*Example* Get all users less than 18 years old or whose `age` field is missing.\n\n r.table("users").filter(r.row["age"] < 18, default=True).run(conn)\n\n*Example* Get all users more than 18 years old. Throw an error if a\ndocument is missing the field `age`.\n\n r.table("users").filter(r.row["age"] > 18, default=r.error()).run(conn)\n\n*Example* Get all users who have given their phone number (all the documents whose field `phone_number` exists and is not `None`).\n\n r.table(\'users\').filter(\n lambda user: user.has_fields(\'phone_number\')\n ).run(conn)\n\n*Example* Get all users with an "editor" role or an "admin" privilege.\n\n r.table(\'users\').filter(\n lambda user: (user[\'role\'] == \'editor\').default(False) |\n (user[\'privilege\'] == \'admin\').default(False)\n ).run(conn)\n\nInstead of using the `default` optional argument to `filter`, we have to use default values on the fields within the `or` clause. Why? If the field on the left side of the `or` clause is missing from a document&mdash;in this case, if the user doesn\'t have a `role` field&mdash;the predicate will generate an error, and will return `False` (or the value the `default` argument is set to) without evaluating the right side of the `or`. By using `.default(False)` on the fields, each side of the `or` will evaluate to either the field\'s value or `False` if the field doesn\'t exist.\n'),
+ (rethinkdb.ast.Table.get, b"table.get(key) -> singleRowSelection\n\nGet a document by primary key.\n\nIf no document exists with that primary key, `get` will return `None`.\n\n*Example* Find a document by UUID.\n\n r.table('posts').get('a9849eef-7176-4411-935b-79a6e3c56a74').run(conn)\n\n*Example* Find a document and merge another document with it.\n\n r.table('heroes').get(3).merge(\n { 'powers': ['invisibility', 'speed'] }\n ).run(conn)\n\n_*Example* Subscribe to a document's [changefeed](http://rethinkdb.com/docs/changefeeds/python).\n\n changes = r.table('heroes').get(3).changes().run(conn)\n"),
+ (rethinkdb.ast.Table.get_all, b"table.get_all(key1[, key2...], [, index='id']) -> selection\n\nGet all documents where the given value matches the value of the requested index.\n\n*Example* Secondary index keys are not guaranteed to be unique so we cannot query via [get](http://rethinkdb.com/api/python/get/) when using a secondary index.\n\n r.table('marvel').get_all('man_of_steel', index='code_name').run(conn)\n\n*Example* Without an index argument, we default to the primary index. While `get` will either return the document or `None` when no document with such a primary key value exists, this will return either a one or zero length stream.\n\n r.table('dc').get_all('superman').run(conn)\n\n*Example* You can get multiple documents in a single call to `get_all`.\n\n r.table('dc').get_all('superman', 'ant man').run(conn)\n\n*Example* You can use [args](http://rethinkdb.com/api/python/args/) with `get_all` to retrieve multiple documents whose keys are in a list. This uses `get_all` to get a list of female superheroes, coerces that to an array, and then gets a list of villains who have those superheroes as enemies.\n\n r.do(\n r.table('heroes').get_all('f', {'index': 'gender'})['id'].coerce_to('array'), \n lamdba heroines: r.table('villains').get_all(r.args(heroines))\n ).run(conn)\n\nSecondary indexes can be used in extremely powerful ways with `get_all` and other commands; read the full article on [secondary indexes](http://rethinkdb.com/docs/secondary-indexes) for examples using boolean operations, `contains` and more.\n"),
+ (rethinkdb.ast.DB.table, b"db.table(name[, use_outdated=False, identifier_format='name']) -> table\n\nReturn all documents in a table. Other commands may be chained after `table` to return a subset of documents (such as `get` and `filter`) or perform further processing.\n\n*Example* Return all documents in the table 'marvel' of the default database.\n\n r.table('marvel').run(conn)\n\n*Example* Return all documents in the table 'marvel' of the database 'heroes'.\n\n r.db('heroes').table('marvel').run(conn)\n\nThere are two optional arguments.\n\n* `use_outdated`: if `True`, this allows potentially out-of-date data to be returned, with potentially faster reads. It also allows you to perform reads from a secondary replica if a primary has failed. Default `False`.\n* `identifier_format`: possible values are `name` and `uuid`, with a default of `name`. If set to `uuid`, then [system tables](http://rethinkdb.com/docs/system-tables/) will refer to servers, databases and tables by UUID rather than name. (This only has an effect when used with system tables.)\n\n*Example* Allow potentially out-of-date data in exchange for faster reads.\n\n r.db('heroes').table('marvel', use_outdated=True).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.downcase, b'string.downcase() -> string\n\nLowercases a string.\n\n*Example*\n\n > r.expr("Sentence about LaTeX.").downcase().run(conn)\n "sentence about latex."\n\n__Note:__ `upcase` and `downcase` only affect ASCII characters.\n'),
+ (rethinkdb.ast.RqlQuery.match, b'string.match(regexp) -> None/object\n\nMatches against a regular expression. If there is a match, returns an object with the fields:\n\n- `str`: The matched string\n- `start`: The matched string\'s start\n- `end`: The matched string\'s end\n- `groups`: The capture groups defined with parentheses\n\nIf no match is found, returns `None`.\n\nAccepts RE2 syntax\n([https://code.google.com/p/re2/wiki/Syntax](https://code.google.com/p/re2/wiki/Syntax)).\nYou can enable case-insensitive matching by prefixing the regular expression with\n`(?i)`. See the linked RE2 documentation for more flags.\n\nThe `match` command does not support backreferences.\n\n*Example* Get all users whose name starts with "A". Because `None` evaluates to `false` in\n`filter`, you can just use the result of `match` for the predicate.\n\n r.table(\'users\').filter(lambda doc:\n doc[\'name\'].match("^A")\n ).run(conn)\n\n*Example* Get all users whose name ends with "n".\n\n r.table(\'users\').filter(lambda doc:\n doc[\'name\'].match("n$")\n ).run(conn)\n\n*Example* Get all users whose name has "li" in it\n\n r.table(\'users\').filter(lambda doc:\n doc[\'name\'].match("li")\n ).run(conn)\n\n*Example* Get all users whose name is "John" with a case-insensitive search.\n\n r.table(\'users\').filter(lambda doc:\n doc[\'name\'].match("(?i)^john$")\n ).run(conn)\n\n*Example* Get all users whose name is composed of only characters between "a" and "z".\n\n r.table(\'users\').filter(lambda doc:\n doc[\'name\'].match("(?i)^[a-z]+$")\n ).run(conn)\n\n*Example* Get all users where the zipcode is a string of 5 digits.\n\n r.table(\'users\').filter(lambda doc:\n doc[\'zipcode\'].match("\\d{5}")\n ).run(conn)\n\n*Example* Retrieve the domain of a basic email\n\n r.expr("name@domain.com").match(".*@(.*)").run(conn)\n\nResult:\n\n {\n "start": 0,\n "end": 20,\n "str": "name@domain.com",\n "groups":[\n {\n "end": 17,\n "start": 7,\n "str": "domain.com"\n }\n ]\n }\n\nYou can then retrieve only the domain with the [\\[\\]](http://rethinkdb.com/api/python/get_field) selector.\n\n r.expr("name@domain.com").match(".*@(.*)")["groups"][0]["str"].run(conn)\n\nReturns `\'domain.com\'`\n\n*Example* Fail to parse out the domain and returns `None`.\n\n r.expr("name[at]domain.com").match(".*@(.*)").run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.split, b'string.split([separator, [max_splits]]) -> array\n\nSplits a string into substrings. Splits on whitespace when called\nwith no arguments. When called with a separator, splits on that\nseparator. When called with a separator and a maximum number of\nsplits, splits on that separator at most `max_splits` times. (Can be\ncalled with `None` as the separator if you want to split on whitespace\nwhile still specifying `max_splits`.)\n\nMimics the behavior of Python\'s `string.split` in edge cases, except\nfor splitting on the empty string, which instead produces an array of\nsingle-character strings.\n\n*Example* Split on whitespace.\n\n > r.expr("foo bar bax").split().run(conn)\n ["foo", "bar", "bax"]\n\n*Example* Split the entries in a CSV file.\n\n > r.expr("12,37,,22,").split(",").run(conn)\n ["12", "37", "", "22", ""]\n\n*Example* Split a string into characters.\n\n > r.expr("mlucy").split("").run(conn)\n ["m", "l", "u", "c", "y"]\n\n*Example* Split the entries in a CSV file, but only at most 3\ntimes.\n\n > r.expr("12,37,,22,").split(",", 3).run(conn)\n ["12", "37", "", "22,"]\n\n*Example* Split on whitespace at most once (i.e. get the first word).\n\n > r.expr("foo bar bax").split(None, 1).run(conn)\n ["foo", "bar bax"]\n'),
+ (rethinkdb.ast.RqlQuery.upcase, b'string.upcase() -> string\n\nUppercases a string.\n\n*Example*\n\n > r.expr("Sentence about LaTeX.").upcase().run(conn)\n "SENTENCE ABOUT LATEX."\n\n__Note:__ `upcase` and `downcase` only affect ASCII characters.\n'),
+ (rethinkdb.ast.RqlQuery.concat_map, b'stream.concat_map(mapping_function) -> stream\narray.concat_map(mapping_function) -> array\n\nConcatenate one or more elements into a single sequence using a mapping function.\n\n`concat_map` works in a similar fashion to `map`, applying the given function to each element in a sequence, but it will always return a single sequence. If the mapping function returns a sequence, `map` would produce a sequence of sequences:\n\n r.expr([1, 2, 3]).map(lambda x: [x, x.mul(2)]).run(conn)\n\nResult:\n\n [[1, 2], [2, 4], [3, 6]]\n\nWhereas `concat_map` with the same mapping function would merge those sequences into one:\n\n r.expr([1, 2, 3]).concat_map(lambda x: [x, x.mul(2)]).run(conn)\n\nResult:\n\n [1, 2, 2, 4, 3, 6]\n\nThe return value, array or stream, will be the same type as the input.\n\n*Example* Construct a sequence of all monsters defeated by Marvel heroes. The field "defeatedMonsters" is an array of one or more monster names.\n\n r.table(\'marvel\').concat_map(lambda hero: hero[\'defeatedMonsters\']).run(conn)\n\n*Example* Simulate an [eq_join](http://rethinkdb.com/api/python/eq_join/) using `concat_map`. (This is how ReQL joins are implemented internally.)\n\n r.table(\'posts\').concat_map(\n lambda post: r.table(\'comments\').get_all(\n post[\'id\'], index=\'post_id\'\n ).map(\n lambda comment: { \'left\': post, \'right\': comment}\n )\n ).run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.is_empty, b"sequence.is_empty() -> bool\n\nTest if a sequence is empty.\n\n*Example* Are there any documents in the marvel table?\n\n r.table('marvel').is_empty().run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.limit, b"sequence.limit(n) -> stream\narray.limit(n) -> array\n\nEnd the sequence after the given number of elements.\n\n*Example* Only so many can fit in our Pantheon of heroes.\n\n r.table('marvel').order_by('belovedness').limit(10).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.map, b"sequence1.map([sequence2, ...], mapping_function) -> stream\narray1.map([sequence2, ...], mapping_function) -> array\nr.map(sequence1[, sequence2, ...], mapping_function) -> stream\nr.map(array1[, array2, ...], mapping_function) -> array\n\nTransform each element of one or more sequences by applying a mapping function to them. If `map` is run with two or more sequences, it will iterate for as many items as there are in the shortest sequence.\n\nNote that `map` can only be applied to sequences, not single values. If you wish to apply a function to a single value/selection (including an array), use the [do](http://rethinkdb.com/api/python/do) command.\n\n*Example* Return the first five squares.\n\n > r.expr([1, 2, 3, 4, 5]).map(lambda val: (val * val)).run(conn)\n \n [1, 4, 9, 16, 25]\n\n*Example* Sum the elements of three sequences.\n\n > sequence1 = [100, 200, 300, 400]\n > sequence2 = [10, 20, 30, 40]\n > sequence3 = [1, 2, 3, 4]\n > r.map(sequence1, sequence2, sequence3,\n lambda val1, val2, val3: (val1 + val2 + val3)).run(conn)\n \n [111, 222, 333, 444]\n\n*Example* Rename a field when retrieving documents using `map` and `merge`.\n\nThis example renames the field `id` to `user_id` when retrieving documents from the table `users`.\n\n r.table('users').map(\n lambda doc: doc.merge({'user_id': doc['id']}).without('id')).run(conn)\n\nNote that in this case, [row](http://rethinkdb.com/api/python/row) may be used as an alternative to writing an anonymous function, as it returns the same value as the function parameter receives:\n\n r.table('users').map(\n r.row.merge({'user_id': r.row['id']}).without('id')).run(conn)\n\n*Example* Assign every superhero an archenemy.\n\n r.table('heroes').map(r.table('villains'),\n lambda hero, villain: hero.merge({'villain': villain})).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.nth, b"sequence.nth(index) -> object\nselection.nth(index) -> selection&lt;object&gt;\n\nGet the *nth* element of a sequence, counting from zero. If the argument is negative, count from the last element.\n\nIn Python, you can use `[]` with an integer as a shorthand for `nth`.\n\n*Example* Select the second element in the array.\n\n r.expr([1,2,3]).nth(1).run(conn)\n r.expr([1,2,3])[1].run(conn)\n\n*Example* Select the bronze medalist from the competitors.\n\n r.table('players').order_by(index=r.desc('score')).nth(3).run(conn)\n\n*Example* Select the last place competitor.\n\n r.table('players').order_by(index=r.desc('score')).nth(-1).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.offsets_of, b"sequence.offsets_of(datum | predicate) -> array\n\nGet the indexes of an element in a sequence. If the argument is a predicate, get the indexes of all elements matching it.\n\n*Example* Find the position of the letter 'c'.\n\n r.expr(['a','b','c']).offsets_of('c').run(conn)\n\n*Example* Find the popularity ranking of invisible heroes.\n\n r.table('marvel').union(r.table('dc')).order_by('popularity').offsets_of(\n r.row['superpowers'].contains('invisibility')\n ).run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.order_by, b'table.order_by([key1...], index=index_name) -> selection<stream>\nselection.order_by(key1, [key2...]) -> selection<array>\nsequence.order_by(key1, [key2...]) -> array\n\nSort the sequence by document values of the given key(s). To specify\nthe ordering, wrap the attribute with either `r.asc` or `r.desc`\n(defaults to ascending).\n\n__Note:__ RethinkDB uses byte-wise ordering for `orderBy` and does not support Unicode collations; non-ASCII characters will be sorted by UTF-8 codepoint. For more information on RethinkDB\'s sorting order, read the section in [ReQL data types](http://rethinkdb.com/docs/data-types/#sorting-order).\n\nSorting without an index requires the server to hold the sequence in\nmemory, and is limited to 100,000 documents (or the setting of the `arrayLimit` option for [run](http://rethinkdb.com/api/python/run)). Sorting with an index can\nbe done on arbitrarily large tables, or after a `between` command\nusing the same index.\n\n*Example* Order all the posts using the index `date`. \n\n r.table(\'posts\').order_by(index=\'date\').run(conn)\n\nThe index must either be the primary key or have been previously created with [index_create](http://rethinkdb.com/api/python/index_create/).\n\n r.table(\'posts\').index_create(\'date\').run(conn)\n\nYou can also select a descending ordering:\n\n r.table(\'posts\').order_by(index=r.desc(\'date\')).run(conn, callback)\n\n*Example* Order a sequence without an index.\n\n r.table(\'posts\').get(1)[\'comments\'].order_by(\'date\')\n\nYou can also select a descending ordering:\n\n r.table(\'posts\').get(1)[\'comments\'].order_by(r.desc(\'date\'))\n\nIf you\'re doing ad-hoc analysis and know your table won\'t have more then 100,000\nelements (or you\'ve changed the setting of the `arrayLimit` option for [run](http://rethinkdb.com/api/python/run)) you can run `order_by` without an index:\n\n r.table(\'small_table\').order_by(\'date\')\n\n*Example* You can efficiently order using multiple fields by using a\n[compound index](http://www.rethinkdb.com/docs/secondary-indexes/python/).\n\nOrder by date and title.\n\n r.table(\'posts\').order_by(index=\'date_and_title\').run(conn)\n\nThe index must have been previously created with [index_create](http://rethinkdb.com/api/python/index_create/).\n\n r.table(\'posts\').index_create(\'date_and_title\', lambda post:\n [post["date"], post["title"]]).run(conn)\n\n_Note_: You cannot specify multiple orders in a compound index. See [issue #2306](https://github.com/rethinkdb/rethinkdb/issues/2306)\nto track progress.\n\n*Example* If you have a sequence with fewer documents than the `array_limit`, you can order it\nby multiple fields without an index.\n\n r.table(\'small_table\').order_by(\'date\', r.desc(\'title\'))\n\n*Example* Notice that an index ordering always has highest\nprecedence. The following query orders posts by date, and if multiple\nposts were published on the same date, they will be ordered by title.\n\n r.table(\'post\').order_by(\'title\', index=\'date\').run(conn)\n*Example* You can use [nested field](http://rethinkdb.com/docs/cookbook/python/#filtering-based-on-nested-fields) syntax to sort on fields from subdocuments. (You can also create indexes on nested fields using this syntax with `index_create`.)\n\n r.table(\'user\').order_by(lambda user: user[\'group\'][\'id\']).run(conn)\n\n*Example* You can efficiently order data on arbitrary expressions using indexes.\n\n r.table(\'posts\').order_by(index=\'votes\').run(conn)\n\nThe index must have been previously created with [index_create](http://rethinkdb.com/api/ruby/index_create/).\n\n r.table(\'posts\').index_create(\'votes\', lambda post:\n post["upvotes"]-post["downvotes"]\n ).run(conn)\n\n*Example* If you have a sequence with fewer documents than the `array_limit`, you can order it with an arbitrary function directly.\n\n r.table(\'small_table\').order_by(lambda doc:\n doc[\'upvotes\']-doc[\'downvotes\']\n );\n\nYou can also select a descending ordering:\n\n r.table(\'small_table\').order_by(r.desc(lambda doc:\n doc[\'upvotes\']-doc[\'downvotes\']\n ));\n\n*Example* Ordering after a `between` command can be done as long as the same index is being used.\n\n r.table("posts").between(r.time(2013, 1, 1, \'+00:00\'), r.time(2013, 1, 1, \'+00:00\'), index=\'date\')\n .order_by(index=\'date\').run(conn);\n\n'),
+ (rethinkdb.ast.RqlQuery.sample, b"sequence.sample(number) -> selection\nstream.sample(number) -> array\narray.sample(number) -> array\n\nSelect a given number of elements from a sequence with uniform random distribution. Selection is done without replacement.\n\nIf the sequence has less than the requested number of elements (i.e., calling `sample(10)` on a sequence with only five elements), `sample` will return the entire sequence in a random order.\n\n*Example* Select 3 random heroes.\n\n r.table('marvel').sample(3).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.skip, b"sequence.skip(n) -> stream\narray.skip(n) -> array\n\nSkip a number of elements from the head of the sequence.\n\n*Example* Here in conjunction with `order_by` we choose to ignore the most successful heroes.\n\n r.table('marvel').order_by('successMetric').skip(10).run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.slice, b"selection.slice(start_index[, end_index, left_bound='closed', right_bound='open']) -> selection\nstream.slice(start_index[, end_index, left_bound='closed', right_bound='open']) -> stream\narray.slice(start_index[, end_index, left_bound='closed', right_bound='open']) -> array\nbinary.slice(start_index[, end_index, left_bound='closed', right_bound='open']) -> binary\n\nReturn the elements of a sequence within the specified range.\n\n`slice` returns the range between `start_index` and `end_index`. If only `start_index` is specified, `slice` returns the range from that index to the end of the sequence. Specify `left_bound` or `right_bound` as `open` or `closed` to indicate whether to include that endpoint of the range by default: `closed` returns that endpoint, while `open` does not. By default, `left_bound` is closed and `right_bound` is open, so the range `(10,13)` will return the tenth, eleventh and twelfth elements in the sequence.\n\nIf `end_index` is past the end of the sequence, all elements from `start_index` to the end of the sequence will be returned. If `start_index` is past the end of the sequence or `end_index` is less than `start_index`, a zero-element sequence will be returned (although see below for negative `end_index` values). An error will be raised on a negative `start_index`.\n\nA negative `end_index` is allowed with arrays; in that case, the returned range counts backward from the array's end. That is, the range of `(2,-1)` returns the second element through the next-to-last element of the range. A negative `end_index` is not allowed with a stream. (An `end_index` of &minus;1 *is* allowed with a stream if `right_bound` is closed; this behaves as if no `end_index` was specified.)\n\nIf `slice` is used with a [binary](http://rethinkdb.com/api/python/binary) object, the indexes refer to byte positions within the object. That is, the range `(10,20)` will refer to the 10th byte through the 19th byte.\n\nIf you are only specifying the indexes and not the bounding options, you may use Python's slice operator as a shorthand: `[start_index:end_index]`.\n\n**Example:** Return the fourth, fifth and sixth youngest players. (The youngest player is at index 0, so those are elements 3&ndash;5.)\n\n r.table('players').order_by(index='age').slice(3,6).run(conn)\n\nOr, using Python's slice operator:\n\n r.table('players').filter({'class': 'amateur'})[10:20].run(conn)\n\n**Example:** Return all but the top three players who have a red flag.\n\n r.table('players').filter({'flag': 'red'}).order_by(index=r.desc('score')).slice(3).run(conn)\n\n**Example:** Return holders of tickets `X` through `Y`, assuming tickets are numbered sequentially. We want to include ticket `Y`.\n\n r.table('users').order_by(index='ticket').slice(x, y, right_bound='closed').run(conn)\n\n**Example:** Return the elements of an array from the second through two from the end (that is, not including the last two).\n\n r.expr([0,1,2,3,4,5]).slice(2,-2).run(conn)\n\nResult:\n\n [2,3]\n"),
+ (rethinkdb.ast.RqlQuery.union, b"stream.union(sequence[, sequence, ...]) -> stream\narray.union(sequence[, sequence, ...]) -> array\n\nConcatenate two or more sequences.\n\n*Example* Construct a stream of all heroes.\n\n r.table('marvel').union(r.table('dc')).run(conn)\n\n*Example* Combine four arrays into one.\n\n r.expr([1, 2]).union([3, 4], [5, 6], [7, 8, 9]).run(conn)\n \n [1, 2, 3, 4, 5, 6, 7, 8, 9]\n"),
+ (rethinkdb.ast.RqlQuery.with_fields, b"sequence.with_fields([selector1, selector2...]) -> stream\narray.with_fields([selector1, selector2...]) -> array\n\nPlucks one or more attributes from a sequence of objects, filtering out any objects in the sequence that do not have the specified fields. Functionally, this is identical to `has_fields` followed by `pluck` on a sequence.\n\n*Example* Get a list of users and their posts, excluding any users who have not made any posts.\n\nExisting table structure:\n\n [\n { 'id': 1, 'user': 'bob', 'email': 'bob@foo.com', 'posts': [ 1, 4, 5 ] },\n { 'id': 2, 'user': 'george', 'email': 'george@foo.com' },\n { 'id': 3, 'user': 'jane', 'email': 'jane@foo.com', 'posts': [ 2, 3, 6 ] }\n ]\n\nCommand and output:\n\n r.table('users').with_fields('id', 'user', 'posts').run(conn)\n \n [\n { 'id': 1, 'user': 'bob', 'posts': [ 1, 4, 5 ] },\n { 'id': 3, 'user': 'jane', 'posts': [ 2, 3, 6 ] }\n ]\n\n*Example* Use the [nested field syntax](http://rethinkdb.com/docs/nested-fields/) to get a list of users with cell phone numbers in their contacts.\n\n r.table('users').with_fields('id', 'user', {contact: {'phone': 'work'}).run(conn)\n"),
+ (rethinkdb.ast.Table.delete, b'table.delete([durability="hard", return_changes=False])\n -> object\nselection.delete([durability="hard", return_changes=False])\n -> object\nsingleSelection.delete([durability="hard", return_changes=False])\n -> object\n\nDelete one or more documents from a table.\n\nThe optional arguments are:\n\n- `durability`: possible values are `hard` and `soft`. This option will override the\ntable or query\'s durability setting (set in [run](http://rethinkdb.com/api/python/run/)). \nIn soft durability mode RethinkDB will acknowledge the write immediately after\nreceiving it, but before the write has been committed to disk.\n- `return_changes`:\n - `True`: return a `changes` array consisting of `old_val`/`new_val` objects describing the changes made, only including the documents actually updated.\n - `False`: do not return a `changes` array (the default).\n - `"always"`: behave as `True`, but include all documents the command tried to update whether or not the update was successful. (This was the behavior of `True` pre-2.0.)\n\nDelete returns an object that contains the following attributes:\n\n- `deleted`: the number of documents that were deleted.\n- `skipped`: the number of documents that were skipped. \nFor example, if you attempt to delete a batch of documents, and another concurrent query\ndeletes some of those documents first, they will be counted as skipped.\n- `errors`: the number of errors encountered while performing the delete.\n- `first_error`: If errors were encountered, contains the text of the first error.\n- `inserted`, `replaced`, and `unchanged`: all 0 for a delete operation.\n- `changes`: if `return_changes` is set to `True`, this will be an array of objects, one for each objected affected by the `delete` operation. Each object will have two keys: `{"new_val": None, "old_val": <old value>}`.\n\n*Example* Delete a single document from the table `comments`.\n\n r.table("comments").get("7eab9e63-73f1-4f33-8ce4-95cbea626f59").delete().run(conn)\n\n*Example* Delete all documents from the table `comments`.\n\n r.table("comments").delete().run(conn)\n\n*Example* Delete all comments where the field `id_post` is `3`.\n\n r.table("comments").filter({"id_post": 3}).delete().run(conn)\n\n*Example* Delete a single document from the table `comments` and return its value.\n\n r.table("comments").get("7eab9e63-73f1-4f33-8ce4-95cbea626f59").delete(return_changes=True).run(conn)\n\nThe result will look like:\n\n {\n "deleted": 1,\n "errors": 0,\n "inserted": 0,\n "changes": [\n {\n "new_val": None,\n "old_val": {\n "id": "7eab9e63-73f1-4f33-8ce4-95cbea626f59",\n "author": "William",\n "comment": "Great post",\n "id_post": 3\n }\n }\n ],\n "replaced": 0,\n "skipped": 0,\n "unchanged": 0\n }\n\n*Example* Delete all documents from the table `comments` without waiting for the\noperation to be flushed to disk.\n\n r.table("comments").delete(durability="soft"}).run(conn)\n'),
+ (rethinkdb.ast.Table.insert, b'table.insert(object | [object1, object2, ...][, durability="hard", return_changes=False, conflict="error"])\n -> object\n\nInsert documents into a table. Accepts a single document or an array of\ndocuments.\n\nThe optional arguments are:\n\n- `durability`: possible values are `hard` and `soft`. This option will override the table or query\'s durability setting (set in [run](http://rethinkdb.com/api/python/run/)). In soft durability mode RethinkDB will acknowledge the write immediately after receiving and caching it, but before the write has been committed to disk.\n- `return_changes`:\n - `True`: return a `changes` array consisting of `old_val`/`new_val` objects describing the changes made, only including the documents actually updated.\n - `False`: do not return a `changes` array (the default).\n - `"always"`: behave as `True`, but include all documents the command tried to update whether or not the update was successful. (This was the behavior of `True` pre-2.0.)\n- `conflict`: Determine handling of inserting documents with the same primary key as existing entries. Possible values are `"error"`, `"replace"` or `"update"`.\n - `"error"`: Do not insert the new document and record the conflict as an error. This is the default.\n - `"replace"`: [Replace](http://rethinkdb.com/api/python/replace/) the old document in its entirety with the new one.\n - `"update"`: [Update](http://rethinkdb.com/api/python/update/) fields of the old document with fields from the new one.\n\nInsert returns an object that contains the following attributes:\n\n- `inserted`: the number of documents successfully inserted.\n- `replaced`: the number of documents updated when `conflict` is set to `"replace"` or `"update"`.\n- `unchanged`: the number of documents whose fields are identical to existing documents with the same primary key when `conflict` is set to `"replace"` or `"update"`.\n- `errors`: the number of errors encountered while performing the insert.\n- `first_error`: If errors were encountered, contains the text of the first error.\n- `deleted` and `skipped`: 0 for an insert operation.\n- `generated_keys`: a list of generated primary keys for inserted documents whose primary keys were not specified (capped to 100,000).\n- `warnings`: if the field `generated_keys` is truncated, you will get the warning _"Too many generated keys (&lt;X&gt;), array truncated to 100000."_.\n- `changes`: if `return_changes` is set to `True`, this will be an array of objects, one for each objected affected by the `insert` operation. Each object will have two keys: `{"new_val": <new value>, "old_val": None}`.\n\n*Example* Insert a document into the table `posts`.\n\n r.table("posts").insert({\n "id": 1,\n "title": "Lorem ipsum",\n "content": "Dolor sit amet"\n }).run(conn)\n\nThe result will be:\n\n {\n "deleted": 0,\n "errors": 0,\n "inserted": 1,\n "replaced": 0,\n "skipped": 0,\n "unchanged": 0\n }\n\n*Example* Insert a document without a defined primary key into the table `posts` where the\nprimary key is `id`.\n\n r.table("posts").insert({\n "title": "Lorem ipsum",\n "content": "Dolor sit amet"\n }).run(conn)\n\nRethinkDB will generate a primary key and return it in `generated_keys`.\n\n {\n "deleted": 0,\n "errors": 0,\n "generated_keys": [\n "dd782b64-70a7-43e4-b65e-dd14ae61d947"\n ],\n "inserted": 1,\n "replaced": 0,\n "skipped": 0,\n "unchanged": 0\n }\n\nRetrieve the document you just inserted with:\n\n r.table("posts").get("dd782b64-70a7-43e4-b65e-dd14ae61d947").run(conn)\n\nAnd you will get back:\n\n {\n "id": "dd782b64-70a7-43e4-b65e-dd14ae61d947",\n "title": "Lorem ipsum",\n "content": "Dolor sit amet",\n }\n\n*Example* Insert multiple documents into the table `users`.\n\n r.table("users").insert([\n {"id": "william", "email": "william@rethinkdb.com"},\n {"id": "lara", "email": "lara@rethinkdb.com"}\n ]).run(conn)\n\n*Example* Insert a document into the table `users`, replacing the document if the document\nalready exists. \n\n r.table("users").insert(\n {"id": "william", "email": "william@rethinkdb.com"},\n conflict="error"\n ).run(conn)\n\n*Example* Copy the documents from `posts` to `posts_backup`.\n\n r.table("posts_backup").insert( r.table("posts") ).run(conn)\n\n*Example* Get back a copy of the inserted document (with its generated primary key).\n\n r.table("posts").insert(\n {"title": "Lorem ipsum", "content": "Dolor sit amet"},\n return_changes=True\n ).run(conn)\n\nThe result will be\n\n {\n "deleted": 0,\n "errors": 0,\n "generated_keys": [\n "dd782b64-70a7-43e4-b65e-dd14ae61d947"\n ],\n "inserted": 1,\n "replaced": 0,\n "skipped": 0,\n "unchanged": 0,\n "changes": [\n {\n "old_val": None,\n "new_val": {\n "id": "dd782b64-70a7-43e4-b65e-dd14ae61d947",\n "title": "Lorem ipsum",\n "content": "Dolor sit amet"\n }\n }\n ]\n }\n'),
+ (rethinkdb.ast.Table.replace, b'table.replace(object | expr[, durability="hard", return_changes=False, non_atomic=False])\n -> object\nselection.replace(object | expr[, durability="hard", return_changes=False, non_atomic=False])\n -> object\nsingleSelection.replace(object | expr[, durability="hard", return_changes=False, non_atomic=False])\n -> object\n\nReplace documents in a table. Accepts a JSON document or a ReQL expression, and replaces\nthe original document with the new one. The new document must have the same primary key\nas the original document.\n\nThe optional arguments are:\n\n- `durability`: possible values are `hard` and `soft`. This option will override the\ntable or query\'s durability setting (set in [run](http://rethinkdb.com/api/python/run/)). \nIn soft durability mode RethinkDB will acknowledge the write immediately after\nreceiving it, but before the write has been committed to disk.\n- `return_changes`:\n - `True`: return a `changes` array consisting of `old_val`/`new_val` objects describing the changes made, only including the documents actually updated.\n - `False`: do not return a `changes` array (the default).\n - `"always"`: behave as `True`, but include all documents the command tried to update whether or not the update was successful. (This was the behavior of `True` pre-2.0.)\n- `non_atomic`: if set to `True`, executes the replacement and distributes the result to replicas in a non-atomic fashion. This flag is required to perform non-deterministic updates, such as those that require reading data from another table.\n\nReplace returns an object that contains the following attributes:\n\n- `replaced`: the number of documents that were replaced\n- `unchanged`: the number of documents that would have been modified, except that the\nnew value was the same as the old value\n- `inserted`: the number of new documents added. You can have new documents inserted if\nyou do a point-replace on a key that isn\'t in the table or you do a replace on a\nselection and one of the documents you are replacing has been deleted\n- `deleted`: the number of deleted documents when doing a replace with `None`\n- `errors`: the number of errors encountered while performing the replace.\n- `first_error`: If errors were encountered, contains the text of the first error.\n- `skipped`: 0 for a replace operation\n- `changes`: if `return_changes` is set to `True`, this will be an array of objects, one for each objected affected by the `replace` operation. Each object will have two keys: `{"new_val": <new value>, "old_val": <old value>}`.\n\n*Example* Replace the document with the primary key `1`.\n\n r.table("posts").get(1).replace({\n "id": 1,\n "title": "Lorem ipsum",\n "content": "Aleas jacta est",\n "status": "draft"\n }).run(conn)\n\n*Example* Remove the field `status` from all posts.\n\n r.table("posts").replace(lambda post:\n post.without("status")\n ).run(conn)\n\n*Example* Remove all the fields that are not `id`, `title` or `content`.\n\n r.table("posts").replace(lambda post:\n post.pluck("id", "title", "content")\n ).run(conn)\n\n*Example* Replace the document with the primary key `1` using soft durability.\n\n r.table("posts").get(1).replace({\n "id": 1,\n "title": "Lorem ipsum",\n "content": "Aleas jacta est",\n "status": "draft"\n }, durability="soft").run(conn)\n\n*Example* Replace the document with the primary key `1` and return the values of the document before\nand after the replace operation.\n\n r.table("posts").get(1).replace({\n "id": 1,\n "title": "Lorem ipsum",\n "content": "Aleas jacta est",\n "status": "published"\n }, return_changes=True).run(conn)\n\nThe result will have a `changes` field:\n\n {\n "deleted": 0,\n "errors": 0,\n "inserted": 0,\n "changes": [\n {\n "new_val": {\n "id":1,\n "title": "Lorem ipsum"\n "content": "Aleas jacta est",\n "status": "published",\n },\n "old_val": {\n "id":1,\n "title": "Lorem ipsum"\n "content": "TODO",\n "status": "draft",\n "author": "William",\n }\n }\n ], \n "replaced": 1,\n "skipped": 0,\n "unchanged": 0\n }\n'),
+ (rethinkdb.ast.Table.sync, b'table.sync() -> object\n\n`sync` ensures that writes on a given table are written to permanent storage. Queries\nthat specify soft durability (`durability=\'soft\'`) do not give such guarantees, so\n`sync` can be used to ensure the state of these queries. A call to `sync` does not return\nuntil all previous writes to the table are persisted.\n\nIf successful, the operation returns an object: `{"synced": 1}`.\n\n*Example* After having updated multiple heroes with soft durability, we now want to wait\nuntil these changes are persisted.\n\n r.table(\'marvel\').sync().run(conn)\n\n'),
+ (rethinkdb.ast.Table.update, b'table.update(object | expr[, durability="hard", return_changes=False, non_atomic=False])\n -> object\nselection.update(object | expr[, durability="hard", return_changes=False, non_atomic=False])\n -> object\nsingleSelection.update(object | expr[, durability="hard", return_changes=False, non_atomic=False])\n -> object\n\nUpdate JSON documents in a table. Accepts a JSON document, a ReQL expression, or a combination of the two.\n\nThe optional arguments are:\n\n- `durability`: possible values are `hard` and `soft`. This option will override the table or query\'s durability setting (set in [run](http://rethinkdb.com/api/python/run/)). In soft durability mode RethinkDB will acknowledge the write immediately after receiving it, but before the write has been committed to disk.\n- `return_changes`:\n - `True`: return a `changes` array consisting of `old_val`/`new_val` objects describing the changes made, only including the documents actually updated.\n - `False`: do not return a `changes` array (the default).\n - `"always"`: behave as `True`, but include all documents the command tried to update whether or not the update was successful. (This was the behavior of `True` pre-2.0.)\n- `non_atomic`: if set to `True`, executes the update and distributes the result to replicas in a non-atomic fashion. This flag is required to perform non-deterministic updates, such as those that require reading data from another table.\n\nUpdate returns an object that contains the following attributes:\n\n- `replaced`: the number of documents that were updated.\n- `unchanged`: the number of documents that would have been modified except the new value was the same as the old value.\n- `skipped`: the number of documents that were skipped because the document didn\'t exist.\n- `errors`: the number of errors encountered while performing the update.\n- `first_error`: If errors were encountered, contains the text of the first error.\n- `deleted` and `inserted`: 0 for an update operation.\n- `changes`: if `return_changes` is set to `True`, this will be an array of objects, one for each objected affected by the `update` operation. Each object will have two keys: `{"new_val": <new value>, "old_val": <old value>}`.\n\n*Example* Update the status of the post with `id` of `1` to `published`.\n\n r.table("posts").get(1).update({"status": "published"}).run(conn)\n\n*Example* Update the status of all posts to `published`.\n\n r.table("posts").update({"status": "published"}).run(conn)\n\n*Example* Update the status of all the posts written by William.\n\n r.table("posts").filter({"author": "William"}).update({"status": "published"}).run(conn)\n\n*Example* Increment the field `view` with `id` of `1`.\nThis query will throw an error if the field `views` doesn\'t exist.\n\n r.table("posts").get(1).update({\n "views": r.row["views"]+1\n }).run(conn)\n\n*Example* Increment the field `view` of the post with `id` of `1`.\nIf the field `views` does not exist, it will be set to `0`.\n\n r.table("posts").update({\n "views": (r.row["views"]+1).default(0)\n }).run(conn)\n\n*Example* Perform a conditional update. \nIf the post has more than 100 views, set the `type` of a post to `hot`, else set it to `normal`.\n\n r.table("posts").get(1).update(lambda post:\n r.branch(\n post["views"] > 100,\n {"type": "hot"},\n {"type": "normal"}\n )\n ).run(conn)\n\n*Example* Update the field `num_comments` with the result of a sub-query. Because this update is not atomic, you must pass the `non_atomic` flag.\n\n r.table("posts").get(1).update({\n "num_comments": r.table("comments").filter({"id_post": 1}).count()\n }, non_atomic=True).run(conn)\n\nIf you forget to specify the `non_atomic` flag, you will get a `RqlRuntimeError`:\n\nRqlRuntimeError: Could not prove function deterministic. Maybe you want to use the non_atomic flag? \n\n*Example* Update the field `num_comments` with a random value between 0 and 100. This update cannot be proven deterministic because of `r.js` (and in fact is not), so you must pass the `non_atomic` flag.\n\n r.table("posts").get(1).update({\n "num_comments": r.js("Math.floor(Math.random()*100)")\n }, non_atomic=True).run(conn)\n\n*Example* Update the status of the post with `id` of `1` using soft durability.\n\n r.table("posts").get(1).update({status: "published"}, durability="soft").run(conn)\n\n*Example* Increment the field `views` and return the values of the document before and after the update operation.\n\n r.table("posts").get(1).update({\n "views": r.row["views"]+1\n }, return_changes=True).run(conn)\n\nThe result will now include a `changes` field:\n\n {\n "deleted": 1,\n "errors": 0,\n "inserted": 0,\n "changes": [\n {\n "new_val": {\n "id": 1,\n "author": "Julius_Caesar",\n "title": "Commentarii de Bello Gallico",\n "content": "Aleas jacta est",\n "views": 207\n },\n "old_val": {\n "id": 1,\n "author": "Julius_Caesar",\n "title": "Commentarii de Bello Gallico",\n "content": "Aleas jacta est",\n "views": 206\n }\n }\n ],\n "replaced": 0,\n "skipped": 0,\n "unchanged": 0\n }\n\nThe `update` command supports RethinkDB\'s [nested field][nf] syntax to update subdocuments. Consider a user table with contact information in this format:\n\n[nf]: /docs/nested-fields/python\n\n {\n "id": 10001,\n "name": "Bob Smith",\n "contact": {\n "phone": {\n "work": "408-555-1212",\n "home": "408-555-1213",\n "cell": "408-555-1214"\n },\n "email": {\n "work": "bob@smith.com",\n "home": "bobsmith@example.com",\n "other": "bobbys@moosecall.net"\n },\n "im": {\n "skype": "Bob Smith",\n "aim": "bobmoose",\n "icq": "nobodyremembersicqnumbers"\n }\n },\n "notes": [\n {\n "date": r.time(2014,1,1,\'Z\'),\n "from": "John Doe",\n "subject": "My name is even more boring than Bob\'s"\n },\n {\n "date": r.time(2014,2,2,\'Z\'),\n "from": "Bob Smith Sr",\n "subject": "Happy Second of February"\n }\n ]\n }\n\n*Example* Update Bob Smith\'s cell phone number.\n\n r.table("users").get(10001).update(\n {"contact": {"phone": {"cell": "408-555-4242"}}}\n ).run(conn)\n\n*Example* Add another note to Bob Smith\'s record.\n\n new_note = {\n "date": r.now(),\n "from": "Inigo Montoya",\n "subject": "You killed my father"\n }\n r.table("users").get(10001).update(\n {"notes": r.row["notes"].append(new_note)}\n ).run(conn)\n\n*Example* Send a note to every user with an ICQ number.\n\n icq_note = {\n "date": r.now(),\n "from": "Admin",\n "subject": "Welcome to the future"\n }\n r.table("users").filter(\n r.row.has_fields({"contact": {"im": "icq"}})\n ).update(\n {"notes": r.row["notes"].append(icq_note)}\n ).run(conn)\n\n*Example* Replace all of Bob\'s IM records. Normally, `update` will merge nested documents together; to replace the entire `"im"` document, use the [literal][] command.\n\n[literal]: /api/python/literal/\n\n r.table(\'users\').get(10001).update(\n {"contact": {"im": r.literal({"aim": "themoosemeister"})}}\n ).run(conn)\n'),
+]
+
+for function, text in docsSource:
+ try:
+ text = str(text.decode('utf-8'))
+ except UnicodeEncodeError:
+ pass
+ if hasattr(function, "__func__"):
+ function.__func__.__doc__ = text
+ else:
+ function.__doc__ = text
diff --git a/ext/librethinkdbxx/reql/ql2.proto b/ext/librethinkdbxx/reql/ql2.proto
new file mode 100644
index 00000000..e40c5be5
--- /dev/null
+++ b/ext/librethinkdbxx/reql/ql2.proto
@@ -0,0 +1,843 @@
+////////////////////////////////////////////////////////////////////////////////
+// THE HIGH-LEVEL VIEW //
+////////////////////////////////////////////////////////////////////////////////
+
+// Process: When you first open a connection, send the magic number
+// for the version of the protobuf you're targeting (in the [Version]
+// enum). This should **NOT** be sent as a protobuf; just send the
+// little-endian 32-bit integer over the wire raw. This number should
+// only be sent once per connection.
+
+// The magic number shall be followed by an authorization key. The
+// first 4 bytes are the length of the key to be sent as a little-endian
+// 32-bit integer, followed by the key string. Even if there is no key,
+// an empty string should be sent (length 0 and no data).
+
+// Following the authorization key, the client shall send a magic number
+// for the communication protocol they want to use (in the [Protocol]
+// enum). This shall be a little-endian 32-bit integer.
+
+// The server will then respond with a NULL-terminated string response.
+// "SUCCESS" indicates that the connection has been accepted. Any other
+// response indicates an error, and the response string should describe
+// the error.
+
+// Next, for each query you want to send, construct a [Query] protobuf
+// and serialize it to a binary blob. Send the blob's size to the
+// server encoded as a little-endian 32-bit integer, followed by the
+// blob itself. You will recieve a [Response] protobuf back preceded
+// by its own size, once again encoded as a little-endian 32-bit
+// integer. You can see an example exchange below in **EXAMPLE**.
+
+// A query consists of a [Term] to evaluate and a unique-per-connection
+// [token].
+
+// Tokens are used for two things:
+// * Keeping track of which responses correspond to which queries.
+// * Batched queries. Some queries return lots of results, so we send back
+// batches of <1000, and you need to send a [CONTINUE] query with the same
+// token to get more results from the original query.
+////////////////////////////////////////////////////////////////////////////////
+
+message VersionDummy { // We need to wrap it like this for some
+ // non-conforming protobuf libraries
+ // This enum contains the magic numbers for your version. See **THE HIGH-LEVEL
+ // VIEW** for what to do with it.
+ enum Version {
+ V0_1 = 0x3f61ba36;
+ V0_2 = 0x723081e1; // Authorization key during handshake
+ V0_3 = 0x5f75e83e; // Authorization key and protocol during handshake
+ V0_4 = 0x400c2d20; // Queries execute in parallel
+ V1_0 = 0x34c2bdc3; // Users and permissions
+ }
+
+ // The protocol to use after the handshake, specified in V0_3
+ enum Protocol {
+ PROTOBUF = 0x271ffc41;
+ JSON = 0x7e6970c7;
+ }
+}
+
+// You send one of:
+// * A [START] query with a [Term] to evaluate and a unique-per-connection token.
+// * A [CONTINUE] query with the same token as a [START] query that returned
+// [SUCCESS_PARTIAL] in its [Response].
+// * A [STOP] query with the same token as a [START] query that you want to stop.
+// * A [NOREPLY_WAIT] query with a unique per-connection token. The server answers
+// with a [WAIT_COMPLETE] [Response].
+// * A [SERVER_INFO] query. The server answers with a [SERVER_INFO] [Response].
+message Query {
+ enum QueryType {
+ START = 1; // Start a new query.
+ CONTINUE = 2; // Continue a query that returned [SUCCESS_PARTIAL]
+ // (see [Response]).
+ STOP = 3; // Stop a query partway through executing.
+ NOREPLY_WAIT = 4; // Wait for noreply operations to finish.
+ SERVER_INFO = 5; // Get server information.
+ }
+ optional QueryType type = 1;
+ // A [Term] is how we represent the operations we want a query to perform.
+ optional Term query = 2; // only present when [type] = [START]
+ optional int64 token = 3;
+ // This flag is ignored on the server. `noreply` should be added
+ // to `global_optargs` instead (the key "noreply" should map to
+ // either true or false).
+ optional bool OBSOLETE_noreply = 4 [default = false];
+
+ // If this is set to [true], then [Datum] values will sometimes be
+ // of [DatumType] [R_JSON] (see below). This can provide enormous
+ // speedups in languages with poor protobuf libraries.
+ optional bool accepts_r_json = 5 [default = false];
+
+ message AssocPair {
+ optional string key = 1;
+ optional Term val = 2;
+ }
+ repeated AssocPair global_optargs = 6;
+}
+
+// A backtrace frame (see `backtrace` in Response below)
+message Frame {
+ enum FrameType {
+ POS = 1; // Error occurred in a positional argument.
+ OPT = 2; // Error occurred in an optional argument.
+ }
+ optional FrameType type = 1;
+ optional int64 pos = 2; // The index of the positional argument.
+ optional string opt = 3; // The name of the optional argument.
+}
+message Backtrace {
+ repeated Frame frames = 1;
+}
+
+// You get back a response with the same [token] as your query.
+message Response {
+ enum ResponseType {
+ // These response types indicate success.
+ SUCCESS_ATOM = 1; // Query returned a single RQL datatype.
+ SUCCESS_SEQUENCE = 2; // Query returned a sequence of RQL datatypes.
+ SUCCESS_PARTIAL = 3; // Query returned a partial sequence of RQL
+ // datatypes. If you send a [CONTINUE] query with
+ // the same token as this response, you will get
+ // more of the sequence. Keep sending [CONTINUE]
+ // queries until you get back [SUCCESS_SEQUENCE].
+ WAIT_COMPLETE = 4; // A [NOREPLY_WAIT] query completed.
+ SERVER_INFO = 5; // The data for a [SERVER_INFO] request. This is
+ // the same as `SUCCESS_ATOM` except that there will
+ // never be profiling data.
+
+ // These response types indicate failure.
+ CLIENT_ERROR = 16; // Means the client is buggy. An example is if the
+ // client sends a malformed protobuf, or tries to
+ // send [CONTINUE] for an unknown token.
+ COMPILE_ERROR = 17; // Means the query failed during parsing or type
+ // checking. For example, if you pass too many
+ // arguments to a function.
+ RUNTIME_ERROR = 18; // Means the query failed at runtime. An example is
+ // if you add together two values from a table, but
+ // they turn out at runtime to be booleans rather
+ // than numbers.
+ }
+ optional ResponseType type = 1;
+
+ // If `ResponseType` is `RUNTIME_ERROR`, this may be filled in with more
+ // information about the error.
+ enum ErrorType {
+ INTERNAL = 1000000;
+ RESOURCE_LIMIT = 2000000;
+ QUERY_LOGIC = 3000000;
+ NON_EXISTENCE = 3100000;
+ OP_FAILED = 4100000;
+ OP_INDETERMINATE = 4200000;
+ USER = 5000000;
+ PERMISSION_ERROR = 6000000;
+ }
+ optional ErrorType error_type = 7;
+
+ // ResponseNotes are used to provide information about the query
+ // response that may be useful for people writing drivers or ORMs.
+ // Currently all the notes we send indicate that a stream has certain
+ // special properties.
+ enum ResponseNote {
+ // The stream is a changefeed stream (e.g. `r.table('test').changes()`).
+ SEQUENCE_FEED = 1;
+ // The stream is a point changefeed stream
+ // (e.g. `r.table('test').get(0).changes()`).
+ ATOM_FEED = 2;
+ // The stream is an order_by_limit changefeed stream
+ // (e.g. `r.table('test').order_by(index: 'id').limit(5).changes()`).
+ ORDER_BY_LIMIT_FEED = 3;
+ // The stream is a union of multiple changefeed types that can't be
+ // collapsed to a single type
+ // (e.g. `r.table('test').changes().union(r.table('test').get(0).changes())`).
+ UNIONED_FEED = 4;
+ // The stream is a changefeed stream and includes notes on what state
+ // the changefeed stream is in (e.g. objects of the form `{state:
+ // 'initializing'}`).
+ INCLUDES_STATES = 5;
+ }
+ repeated ResponseNote notes = 6;
+
+ optional int64 token = 2; // Indicates what [Query] this response corresponds to.
+
+ // [response] contains 1 RQL datum if [type] is [SUCCESS_ATOM] or
+ // [SERVER_INFO]. [response] contains many RQL data if [type] is
+ // [SUCCESS_SEQUENCE] or [SUCCESS_PARTIAL]. [response] contains 1
+ // error message (of type [R_STR]) in all other cases.
+ repeated Datum response = 3;
+
+ // If [type] is [CLIENT_ERROR], [TYPE_ERROR], or [RUNTIME_ERROR], then a
+ // backtrace will be provided. The backtrace says where in the query the
+ // error occurred. Ideally this information will be presented to the user as
+ // a pretty-printed version of their query with the erroneous section
+ // underlined. A backtrace is a series of 0 or more [Frame]s, each of which
+ // specifies either the index of a positional argument or the name of an
+ // optional argument. (Those words will make more sense if you look at the
+ // [Term] message below.)
+ optional Backtrace backtrace = 4; // Contains n [Frame]s when you get back an error.
+
+ // If the [global_optargs] in the [Query] that this [Response] is a
+ // response to contains a key "profile" which maps to a static value of
+ // true then [profile] will contain a [Datum] which provides profiling
+ // information about the execution of the query. This field should be
+ // returned to the user along with the result that would normally be
+ // returned (a datum or a cursor). In official drivers this is accomplished
+ // by putting them inside of an object with "value" mapping to the return
+ // value and "profile" mapping to the profile object.
+ optional Datum profile = 5;
+}
+
+// A [Datum] is a chunk of data that can be serialized to disk or returned to
+// the user in a Response. Currently we only support JSON types, but we may
+// support other types in the future (e.g., a date type or an integer type).
+message Datum {
+ enum DatumType {
+ R_NULL = 1;
+ R_BOOL = 2;
+ R_NUM = 3; // a double
+ R_STR = 4;
+ R_ARRAY = 5;
+ R_OBJECT = 6;
+ // This [DatumType] will only be used if [accepts_r_json] is
+ // set to [true] in [Query]. [r_str] will be filled with a
+ // JSON encoding of the [Datum].
+ R_JSON = 7; // uses r_str
+ }
+ optional DatumType type = 1;
+ optional bool r_bool = 2;
+ optional double r_num = 3;
+ optional string r_str = 4;
+
+ repeated Datum r_array = 5;
+ message AssocPair {
+ optional string key = 1;
+ optional Datum val = 2;
+ }
+ repeated AssocPair r_object = 6;
+}
+
+// A [Term] is either a piece of data (see **Datum** above), or an operator and
+// its operands. If you have a [Datum], it's stored in the member [datum]. If
+// you have an operator, its positional arguments are stored in [args] and its
+// optional arguments are stored in [optargs].
+//
+// A note about type signatures:
+// We use the following notation to denote types:
+// arg1_type, arg2_type, argrest_type... -> result_type
+// So, for example, if we have a function `avg` that takes any number of
+// arguments and averages them, we might write:
+// NUMBER... -> NUMBER
+// Or if we had a function that took one number modulo another:
+// NUMBER, NUMBER -> NUMBER
+// Or a function that takes a table and a primary key of any Datum type, then
+// retrieves the entry with that primary key:
+// Table, DATUM -> OBJECT
+// Some arguments must be provided as literal values (and not the results of sub
+// terms). These are marked with a `!`.
+// Optional arguments are specified within curly braces as argname `:` value
+// type (e.x `{noreply:BOOL}`)
+// Many RQL operations are polymorphic. For these, alterantive type signatures
+// are separated by `|`.
+//
+// The RQL type hierarchy is as follows:
+// Top
+// DATUM
+// NULL
+// BOOL
+// NUMBER
+// STRING
+// OBJECT
+// SingleSelection
+// ARRAY
+// Sequence
+// ARRAY
+// Stream
+// StreamSelection
+// Table
+// Database
+// Function
+// Ordering - used only by ORDER_BY
+// Pathspec -- an object, string, or array that specifies a path
+// Error
+message Term {
+ enum TermType {
+ // A RQL datum, stored in `datum` below.
+ DATUM = 1;
+
+ MAKE_ARRAY = 2; // DATUM... -> ARRAY
+ // Evaluate the terms in [optargs] and make an object
+ MAKE_OBJ = 3; // {...} -> OBJECT
+
+ // * Compound types
+
+ // Takes an integer representing a variable and returns the value stored
+ // in that variable. It's the responsibility of the client to translate
+ // from their local representation of a variable to a unique _non-negative_
+ // integer for that variable. (We do it this way instead of letting
+ // clients provide variable names as strings to discourage
+ // variable-capturing client libraries, and because it's more efficient
+ // on the wire.)
+ VAR = 10; // !NUMBER -> DATUM
+ // Takes some javascript code and executes it.
+ JAVASCRIPT = 11; // STRING {timeout: !NUMBER} -> DATUM |
+ // STRING {timeout: !NUMBER} -> Function(*)
+ UUID = 169; // () -> DATUM
+
+ // Takes an HTTP URL and gets it. If the get succeeds and
+ // returns valid JSON, it is converted into a DATUM
+ HTTP = 153; // STRING {data: OBJECT | STRING,
+ // timeout: !NUMBER,
+ // method: STRING,
+ // params: OBJECT,
+ // header: OBJECT | ARRAY,
+ // attempts: NUMBER,
+ // redirects: NUMBER,
+ // verify: BOOL,
+ // page: FUNC | STRING,
+ // page_limit: NUMBER,
+ // auth: OBJECT,
+ // result_format: STRING,
+ // } -> STRING | STREAM
+
+ // Takes a string and throws an error with that message.
+ // Inside of a `default` block, you can omit the first
+ // argument to rethrow whatever error you catch (this is most
+ // useful as an argument to the `default` filter optarg).
+ ERROR = 12; // STRING -> Error | -> Error
+ // Takes nothing and returns a reference to the implicit variable.
+ IMPLICIT_VAR = 13; // -> DATUM
+
+ // * Data Operators
+ // Returns a reference to a database.
+ DB = 14; // STRING -> Database
+ // Returns a reference to a table.
+ TABLE = 15; // Database, STRING, {read_mode:STRING, identifier_format:STRING} -> Table
+ // STRING, {read_mode:STRING, identifier_format:STRING} -> Table
+ // Gets a single element from a table by its primary or a secondary key.
+ GET = 16; // Table, STRING -> SingleSelection | Table, NUMBER -> SingleSelection |
+ // Table, STRING -> NULL | Table, NUMBER -> NULL |
+ GET_ALL = 78; // Table, DATUM..., {index:!STRING} => ARRAY
+
+ // Simple DATUM Ops
+ EQ = 17; // DATUM... -> BOOL
+ NE = 18; // DATUM... -> BOOL
+ LT = 19; // DATUM... -> BOOL
+ LE = 20; // DATUM... -> BOOL
+ GT = 21; // DATUM... -> BOOL
+ GE = 22; // DATUM... -> BOOL
+ NOT = 23; // BOOL -> BOOL
+ // ADD can either add two numbers or concatenate two arrays.
+ ADD = 24; // NUMBER... -> NUMBER | STRING... -> STRING
+ SUB = 25; // NUMBER... -> NUMBER
+ MUL = 26; // NUMBER... -> NUMBER
+ DIV = 27; // NUMBER... -> NUMBER
+ MOD = 28; // NUMBER, NUMBER -> NUMBER
+
+ FLOOR = 183; // NUMBER -> NUMBER
+ CEIL = 184; // NUMBER -> NUMBER
+ ROUND = 185; // NUMBER -> NUMBER
+
+ // DATUM Array Ops
+ // Append a single element to the end of an array (like `snoc`).
+ APPEND = 29; // ARRAY, DATUM -> ARRAY
+ // Prepend a single element to the end of an array (like `cons`).
+ PREPEND = 80; // ARRAY, DATUM -> ARRAY
+ //Remove the elements of one array from another array.
+ DIFFERENCE = 95; // ARRAY, ARRAY -> ARRAY
+
+ // DATUM Set Ops
+ // Set ops work on arrays. They don't use actual sets and thus have
+ // performance characteristics you would expect from arrays rather than
+ // from sets. All set operations have the post condition that they
+ // array they return contains no duplicate values.
+ SET_INSERT = 88; // ARRAY, DATUM -> ARRAY
+ SET_INTERSECTION = 89; // ARRAY, ARRAY -> ARRAY
+ SET_UNION = 90; // ARRAY, ARRAY -> ARRAY
+ SET_DIFFERENCE = 91; // ARRAY, ARRAY -> ARRAY
+
+ SLICE = 30; // Sequence, NUMBER, NUMBER -> Sequence
+ SKIP = 70; // Sequence, NUMBER -> Sequence
+ LIMIT = 71; // Sequence, NUMBER -> Sequence
+ OFFSETS_OF = 87; // Sequence, DATUM -> Sequence | Sequence, Function(1) -> Sequence
+ CONTAINS = 93; // Sequence, (DATUM | Function(1))... -> BOOL
+
+ // Stream/Object Ops
+ // Get a particular field from an object, or map that over a
+ // sequence.
+ GET_FIELD = 31; // OBJECT, STRING -> DATUM
+ // | Sequence, STRING -> Sequence
+ // Return an array containing the keys of the object.
+ KEYS = 94; // OBJECT -> ARRAY
+ // Return an array containing the values of the object.
+ VALUES = 186; // OBJECT -> ARRAY
+ // Creates an object
+ OBJECT = 143; // STRING, DATUM, ... -> OBJECT
+ // Check whether an object contains all the specified fields,
+ // or filters a sequence so that all objects inside of it
+ // contain all the specified fields.
+ HAS_FIELDS = 32; // OBJECT, Pathspec... -> BOOL
+ // x.with_fields(...) <=> x.has_fields(...).pluck(...)
+ WITH_FIELDS = 96; // Sequence, Pathspec... -> Sequence
+ // Get a subset of an object by selecting some attributes to preserve,
+ // or map that over a sequence. (Both pick and pluck, polymorphic.)
+ PLUCK = 33; // Sequence, Pathspec... -> Sequence | OBJECT, Pathspec... -> OBJECT
+ // Get a subset of an object by selecting some attributes to discard, or
+ // map that over a sequence. (Both unpick and without, polymorphic.)
+ WITHOUT = 34; // Sequence, Pathspec... -> Sequence | OBJECT, Pathspec... -> OBJECT
+ // Merge objects (right-preferential)
+ MERGE = 35; // OBJECT... -> OBJECT | Sequence -> Sequence
+
+ // Sequence Ops
+ // Get all elements of a sequence between two values.
+ // Half-open by default, but the openness of either side can be
+ // changed by passing 'closed' or 'open for `right_bound` or
+ // `left_bound`.
+ BETWEEN_DEPRECATED = 36; // Deprecated version of between, which allows `null` to specify unboundedness
+ // With the newer version, clients should use `r.minval` and `r.maxval` for unboundedness
+ BETWEEN = 182; // StreamSelection, DATUM, DATUM, {index:!STRING, right_bound:STRING, left_bound:STRING} -> StreamSelection
+ REDUCE = 37; // Sequence, Function(2) -> DATUM
+ MAP = 38; // Sequence, Function(1) -> Sequence
+ // The arity of the function should be
+ // Sequence..., Function(sizeof...(Sequence)) -> Sequence
+
+ FOLD = 187; // Sequence, Datum, Function(2), {Function(3), Function(1)
+
+ // Filter a sequence with either a function or a shortcut
+ // object (see API docs for details). The body of FILTER is
+ // wrapped in an implicit `.default(false)`, and you can
+ // change the default value by specifying the `default`
+ // optarg. If you make the default `r.error`, all errors
+ // caught by `default` will be rethrown as if the `default`
+ // did not exist.
+ FILTER = 39; // Sequence, Function(1), {default:DATUM} -> Sequence |
+ // Sequence, OBJECT, {default:DATUM} -> Sequence
+ // Map a function over a sequence and then concatenate the results together.
+ CONCAT_MAP = 40; // Sequence, Function(1) -> Sequence
+ // Order a sequence based on one or more attributes.
+ ORDER_BY = 41; // Sequence, (!STRING | Ordering)..., {index: (!STRING | Ordering)} -> Sequence
+ // Get all distinct elements of a sequence (like `uniq`).
+ DISTINCT = 42; // Sequence -> Sequence
+ // Count the number of elements in a sequence, or only the elements that match
+ // a given filter.
+ COUNT = 43; // Sequence -> NUMBER | Sequence, DATUM -> NUMBER | Sequence, Function(1) -> NUMBER
+ IS_EMPTY = 86; // Sequence -> BOOL
+ // Take the union of multiple sequences (preserves duplicate elements! (use distinct)).
+ UNION = 44; // Sequence... -> Sequence
+ // Get the Nth element of a sequence.
+ NTH = 45; // Sequence, NUMBER -> DATUM
+ // do NTH or GET_FIELD depending on target object
+ BRACKET = 170; // Sequence | OBJECT, NUMBER | STRING -> DATUM
+ // OBSOLETE_GROUPED_MAPREDUCE = 46;
+ // OBSOLETE_GROUPBY = 47;
+
+ INNER_JOIN = 48; // Sequence, Sequence, Function(2) -> Sequence
+ OUTER_JOIN = 49; // Sequence, Sequence, Function(2) -> Sequence
+ // An inner-join that does an equality comparison on two attributes.
+ EQ_JOIN = 50; // Sequence, !STRING, Sequence, {index:!STRING} -> Sequence
+ ZIP = 72; // Sequence -> Sequence
+ RANGE = 173; // -> Sequence [0, +inf)
+ // NUMBER -> Sequence [0, a)
+ // NUMBER, NUMBER -> Sequence [a, b)
+
+ // Array Ops
+ // Insert an element in to an array at a given index.
+ INSERT_AT = 82; // ARRAY, NUMBER, DATUM -> ARRAY
+ // Remove an element at a given index from an array.
+ DELETE_AT = 83; // ARRAY, NUMBER -> ARRAY |
+ // ARRAY, NUMBER, NUMBER -> ARRAY
+ // Change the element at a given index of an array.
+ CHANGE_AT = 84; // ARRAY, NUMBER, DATUM -> ARRAY
+ // Splice one array in to another array.
+ SPLICE_AT = 85; // ARRAY, NUMBER, ARRAY -> ARRAY
+
+ // * Type Ops
+ // Coerces a datum to a named type (e.g. "bool").
+ // If you previously used `stream_to_array`, you should use this instead
+ // with the type "array".
+ COERCE_TO = 51; // Top, STRING -> Top
+ // Returns the named type of a datum (e.g. TYPE_OF(true) = "BOOL")
+ TYPE_OF = 52; // Top -> STRING
+
+ // * Write Ops (the OBJECTs contain data about number of errors etc.)
+ // Updates all the rows in a selection. Calls its Function with the row
+ // to be updated, and then merges the result of that call.
+ UPDATE = 53; // StreamSelection, Function(1), {non_atomic:BOOL, durability:STRING, return_changes:BOOL} -> OBJECT |
+ // SingleSelection, Function(1), {non_atomic:BOOL, durability:STRING, return_changes:BOOL} -> OBJECT |
+ // StreamSelection, OBJECT, {non_atomic:BOOL, durability:STRING, return_changes:BOOL} -> OBJECT |
+ // SingleSelection, OBJECT, {non_atomic:BOOL, durability:STRING, return_changes:BOOL} -> OBJECT
+ // Deletes all the rows in a selection.
+ DELETE = 54; // StreamSelection, {durability:STRING, return_changes:BOOL} -> OBJECT | SingleSelection -> OBJECT
+ // Replaces all the rows in a selection. Calls its Function with the row
+ // to be replaced, and then discards it and stores the result of that
+ // call.
+ REPLACE = 55; // StreamSelection, Function(1), {non_atomic:BOOL, durability:STRING, return_changes:BOOL} -> OBJECT | SingleSelection, Function(1), {non_atomic:BOOL, durability:STRING, return_changes:BOOL} -> OBJECT
+ // Inserts into a table. If `conflict` is replace, overwrites
+ // entries with the same primary key. If `conflict` is
+ // update, does an update on the entry. If `conflict` is
+ // error, or is omitted, conflicts will trigger an error.
+ INSERT = 56; // Table, OBJECT, {conflict:STRING, durability:STRING, return_changes:BOOL} -> OBJECT | Table, Sequence, {conflict:STRING, durability:STRING, return_changes:BOOL} -> OBJECT
+
+ // * Administrative OPs
+ // Creates a database with a particular name.
+ DB_CREATE = 57; // STRING -> OBJECT
+ // Drops a database with a particular name.
+ DB_DROP = 58; // STRING -> OBJECT
+ // Lists all the databases by name. (Takes no arguments)
+ DB_LIST = 59; // -> ARRAY
+ // Creates a table with a particular name in a particular
+ // database. (You may omit the first argument to use the
+ // default database.)
+ TABLE_CREATE = 60; // Database, STRING, {primary_key:STRING, shards:NUMBER, replicas:NUMBER, primary_replica_tag:STRING} -> OBJECT
+ // Database, STRING, {primary_key:STRING, shards:NUMBER, replicas:OBJECT, primary_replica_tag:STRING} -> OBJECT
+ // STRING, {primary_key:STRING, shards:NUMBER, replicas:NUMBER, primary_replica_tag:STRING} -> OBJECT
+ // STRING, {primary_key:STRING, shards:NUMBER, replicas:OBJECT, primary_replica_tag:STRING} -> OBJECT
+ // Drops a table with a particular name from a particular
+ // database. (You may omit the first argument to use the
+ // default database.)
+ TABLE_DROP = 61; // Database, STRING -> OBJECT
+ // STRING -> OBJECT
+ // Lists all the tables in a particular database. (You may
+ // omit the first argument to use the default database.)
+ TABLE_LIST = 62; // Database -> ARRAY
+ // -> ARRAY
+ // Returns the row in the `rethinkdb.table_config` or `rethinkdb.db_config` table
+ // that corresponds to the given database or table.
+ CONFIG = 174; // Database -> SingleSelection
+ // Table -> SingleSelection
+ // Returns the row in the `rethinkdb.table_status` table that corresponds to the
+ // given table.
+ STATUS = 175; // Table -> SingleSelection
+ // Called on a table, waits for that table to be ready for read/write operations.
+ // Called on a database, waits for all of the tables in the database to be ready.
+ // Returns the corresponding row or rows from the `rethinkdb.table_status` table.
+ WAIT = 177; // Table -> OBJECT
+ // Database -> OBJECT
+ // Generates a new config for the given table, or all tables in the given database
+ // The `shards` and `replicas` arguments are required. If `emergency_repair` is
+ // specified, it will enter a completely different mode of repairing a table
+ // which has lost half or more of its replicas.
+ RECONFIGURE = 176; // Database|Table, {shards:NUMBER, replicas:NUMBER [,
+ // dry_run:BOOLEAN]
+ // } -> OBJECT
+ // Database|Table, {shards:NUMBER, replicas:OBJECT [,
+ // primary_replica_tag:STRING,
+ // nonvoting_replica_tags:ARRAY,
+ // dry_run:BOOLEAN]
+ // } -> OBJECT
+ // Table, {emergency_repair:STRING, dry_run:BOOLEAN} -> OBJECT
+ // Balances the table's shards but leaves everything else the same. Can also be
+ // applied to an entire database at once.
+ REBALANCE = 179; // Table -> OBJECT
+ // Database -> OBJECT
+
+ // Ensures that previously issued soft-durability writes are complete and
+ // written to disk.
+ SYNC = 138; // Table -> OBJECT
+
+ // Set global, database, or table-specific permissions
+ GRANT = 188; // -> OBJECT
+ // Database -> OBJECT
+ // Table -> OBJECT
+
+ // * Secondary indexes OPs
+ // Creates a new secondary index with a particular name and definition.
+ INDEX_CREATE = 75; // Table, STRING, Function(1), {multi:BOOL} -> OBJECT
+ // Drops a secondary index with a particular name from the specified table.
+ INDEX_DROP = 76; // Table, STRING -> OBJECT
+ // Lists all secondary indexes on a particular table.
+ INDEX_LIST = 77; // Table -> ARRAY
+ // Gets information about whether or not a set of indexes are ready to
+ // be accessed. Returns a list of objects that look like this:
+ // {index:STRING, ready:BOOL[, progress:NUMBER]}
+ INDEX_STATUS = 139; // Table, STRING... -> ARRAY
+ // Blocks until a set of indexes are ready to be accessed. Returns the
+ // same values INDEX_STATUS.
+ INDEX_WAIT = 140; // Table, STRING... -> ARRAY
+ // Renames the given index to a new name
+ INDEX_RENAME = 156; // Table, STRING, STRING, {overwrite:BOOL} -> OBJECT
+
+ // * Control Operators
+ // Calls a function on data
+ FUNCALL = 64; // Function(*), DATUM... -> DATUM
+ // Executes its first argument, and returns its second argument if it
+ // got [true] or its third argument if it got [false] (like an `if`
+ // statement).
+ BRANCH = 65; // BOOL, Top, Top -> Top
+ // Returns true if any of its arguments returns true (short-circuits).
+ OR = 66; // BOOL... -> BOOL
+ // Returns true if all of its arguments return true (short-circuits).
+ AND = 67; // BOOL... -> BOOL
+ // Calls its Function with each entry in the sequence
+ // and executes the array of terms that Function returns.
+ FOR_EACH = 68; // Sequence, Function(1) -> OBJECT
+
+////////////////////////////////////////////////////////////////////////////////
+////////// Special Terms
+////////////////////////////////////////////////////////////////////////////////
+
+ // An anonymous function. Takes an array of numbers representing
+ // variables (see [VAR] above), and a [Term] to execute with those in
+ // scope. Returns a function that may be passed an array of arguments,
+ // then executes the Term with those bound to the variable names. The
+ // user will never construct this directly. We use it internally for
+ // things like `map` which take a function. The "arity" of a [Function] is
+ // the number of arguments it takes.
+ // For example, here's what `_X_.map{|x| x+2}` turns into:
+ // Term {
+ // type = MAP;
+ // args = [_X_,
+ // Term {
+ // type = Function;
+ // args = [Term {
+ // type = DATUM;
+ // datum = Datum {
+ // type = R_ARRAY;
+ // r_array = [Datum { type = R_NUM; r_num = 1; }];
+ // };
+ // },
+ // Term {
+ // type = ADD;
+ // args = [Term {
+ // type = VAR;
+ // args = [Term {
+ // type = DATUM;
+ // datum = Datum { type = R_NUM;
+ // r_num = 1};
+ // }];
+ // },
+ // Term {
+ // type = DATUM;
+ // datum = Datum { type = R_NUM; r_num = 2; };
+ // }];
+ // }];
+ // }];
+ FUNC = 69; // ARRAY, Top -> ARRAY -> Top
+
+ // Indicates to ORDER_BY that this attribute is to be sorted in ascending order.
+ ASC = 73; // !STRING -> Ordering
+ // Indicates to ORDER_BY that this attribute is to be sorted in descending order.
+ DESC = 74; // !STRING -> Ordering
+
+ // Gets info about anything. INFO is most commonly called on tables.
+ INFO = 79; // Top -> OBJECT
+
+ // `a.match(b)` returns a match object if the string `a`
+ // matches the regular expression `b`.
+ MATCH = 97; // STRING, STRING -> DATUM
+
+ // Change the case of a string.
+ UPCASE = 141; // STRING -> STRING
+ DOWNCASE = 142; // STRING -> STRING
+
+ // Select a number of elements from sequence with uniform distribution.
+ SAMPLE = 81; // Sequence, NUMBER -> Sequence
+
+ // Evaluates its first argument. If that argument returns
+ // NULL or throws an error related to the absence of an
+ // expected value (for instance, accessing a non-existent
+ // field or adding NULL to an integer), DEFAULT will either
+ // return its second argument or execute it if it's a
+ // function. If the second argument is a function, it will be
+ // passed either the text of the error or NULL as its
+ // argument.
+ DEFAULT = 92; // Top, Top -> Top
+
+ // Parses its first argument as a json string and returns it as a
+ // datum.
+ JSON = 98; // STRING -> DATUM
+ // Returns the datum as a JSON string.
+ // N.B.: we would really prefer this be named TO_JSON and that exists as
+ // an alias in Python and JavaScript drivers; however it conflicts with the
+ // standard `to_json` method defined by Ruby's standard json library.
+ TO_JSON_STRING = 172; // DATUM -> STRING
+
+ // Parses its first arguments as an ISO 8601 time and returns it as a
+ // datum.
+ ISO8601 = 99; // STRING -> PSEUDOTYPE(TIME)
+ // Prints a time as an ISO 8601 time.
+ TO_ISO8601 = 100; // PSEUDOTYPE(TIME) -> STRING
+
+ // Returns a time given seconds since epoch in UTC.
+ EPOCH_TIME = 101; // NUMBER -> PSEUDOTYPE(TIME)
+ // Returns seconds since epoch in UTC given a time.
+ TO_EPOCH_TIME = 102; // PSEUDOTYPE(TIME) -> NUMBER
+
+ // The time the query was received by the server.
+ NOW = 103; // -> PSEUDOTYPE(TIME)
+ // Puts a time into an ISO 8601 timezone.
+ IN_TIMEZONE = 104; // PSEUDOTYPE(TIME), STRING -> PSEUDOTYPE(TIME)
+ // a.during(b, c) returns whether a is in the range [b, c)
+ DURING = 105; // PSEUDOTYPE(TIME), PSEUDOTYPE(TIME), PSEUDOTYPE(TIME) -> BOOL
+ // Retrieves the date portion of a time.
+ DATE = 106; // PSEUDOTYPE(TIME) -> PSEUDOTYPE(TIME)
+ // x.time_of_day == x.date - x
+ TIME_OF_DAY = 126; // PSEUDOTYPE(TIME) -> NUMBER
+ // Returns the timezone of a time.
+ TIMEZONE = 127; // PSEUDOTYPE(TIME) -> STRING
+
+ // These access the various components of a time.
+ YEAR = 128; // PSEUDOTYPE(TIME) -> NUMBER
+ MONTH = 129; // PSEUDOTYPE(TIME) -> NUMBER
+ DAY = 130; // PSEUDOTYPE(TIME) -> NUMBER
+ DAY_OF_WEEK = 131; // PSEUDOTYPE(TIME) -> NUMBER
+ DAY_OF_YEAR = 132; // PSEUDOTYPE(TIME) -> NUMBER
+ HOURS = 133; // PSEUDOTYPE(TIME) -> NUMBER
+ MINUTES = 134; // PSEUDOTYPE(TIME) -> NUMBER
+ SECONDS = 135; // PSEUDOTYPE(TIME) -> NUMBER
+
+ // Construct a time from a date and optional timezone or a
+ // date+time and optional timezone.
+ TIME = 136; // NUMBER, NUMBER, NUMBER, STRING -> PSEUDOTYPE(TIME) |
+ // NUMBER, NUMBER, NUMBER, NUMBER, NUMBER, NUMBER, STRING -> PSEUDOTYPE(TIME) |
+
+ // Constants for ISO 8601 days of the week.
+ MONDAY = 107; // -> 1
+ TUESDAY = 108; // -> 2
+ WEDNESDAY = 109; // -> 3
+ THURSDAY = 110; // -> 4
+ FRIDAY = 111; // -> 5
+ SATURDAY = 112; // -> 6
+ SUNDAY = 113; // -> 7
+
+ // Constants for ISO 8601 months.
+ JANUARY = 114; // -> 1
+ FEBRUARY = 115; // -> 2
+ MARCH = 116; // -> 3
+ APRIL = 117; // -> 4
+ MAY = 118; // -> 5
+ JUNE = 119; // -> 6
+ JULY = 120; // -> 7
+ AUGUST = 121; // -> 8
+ SEPTEMBER = 122; // -> 9
+ OCTOBER = 123; // -> 10
+ NOVEMBER = 124; // -> 11
+ DECEMBER = 125; // -> 12
+
+ // Indicates to MERGE to replace, or remove in case of an empty literal, the
+ // other object rather than merge it.
+ LITERAL = 137; // -> Merging
+ // JSON -> Merging
+
+ // SEQUENCE, STRING -> GROUPED_SEQUENCE | SEQUENCE, FUNCTION -> GROUPED_SEQUENCE
+ GROUP = 144;
+ SUM = 145;
+ AVG = 146;
+ MIN = 147;
+ MAX = 148;
+
+ // `str.split()` splits on whitespace
+ // `str.split(" ")` splits on spaces only
+ // `str.split(" ", 5)` splits on spaces with at most 5 results
+ // `str.split(nil, 5)` splits on whitespace with at most 5 results
+ SPLIT = 149; // STRING -> ARRAY | STRING, STRING -> ARRAY | STRING, STRING, NUMBER -> ARRAY | STRING, NULL, NUMBER -> ARRAY
+
+ UNGROUP = 150; // GROUPED_DATA -> ARRAY
+
+ // Takes a range of numbers and returns a random number within the range
+ RANDOM = 151; // NUMBER, NUMBER {float:BOOL} -> DATUM
+
+ CHANGES = 152; // TABLE -> STREAM
+ ARGS = 154; // ARRAY -> SPECIAL (used to splice arguments)
+
+ // BINARY is client-only at the moment, it is not supported on the server
+ BINARY = 155; // STRING -> PSEUDOTYPE(BINARY)
+
+ GEOJSON = 157; // OBJECT -> PSEUDOTYPE(GEOMETRY)
+ TO_GEOJSON = 158; // PSEUDOTYPE(GEOMETRY) -> OBJECT
+ POINT = 159; // NUMBER, NUMBER -> PSEUDOTYPE(GEOMETRY)
+ LINE = 160; // (ARRAY | PSEUDOTYPE(GEOMETRY))... -> PSEUDOTYPE(GEOMETRY)
+ POLYGON = 161; // (ARRAY | PSEUDOTYPE(GEOMETRY))... -> PSEUDOTYPE(GEOMETRY)
+ DISTANCE = 162; // PSEUDOTYPE(GEOMETRY), PSEUDOTYPE(GEOMETRY) {geo_system:STRING, unit:STRING} -> NUMBER
+ INTERSECTS = 163; // PSEUDOTYPE(GEOMETRY), PSEUDOTYPE(GEOMETRY) -> BOOL
+ INCLUDES = 164; // PSEUDOTYPE(GEOMETRY), PSEUDOTYPE(GEOMETRY) -> BOOL
+ CIRCLE = 165; // PSEUDOTYPE(GEOMETRY), NUMBER {num_vertices:NUMBER, geo_system:STRING, unit:STRING, fill:BOOL} -> PSEUDOTYPE(GEOMETRY)
+ GET_INTERSECTING = 166; // TABLE, PSEUDOTYPE(GEOMETRY) {index:!STRING} -> StreamSelection
+ FILL = 167; // PSEUDOTYPE(GEOMETRY) -> PSEUDOTYPE(GEOMETRY)
+ GET_NEAREST = 168; // TABLE, PSEUDOTYPE(GEOMETRY) {index:!STRING, max_results:NUM, max_dist:NUM, geo_system:STRING, unit:STRING} -> ARRAY
+ POLYGON_SUB = 171; // PSEUDOTYPE(GEOMETRY), PSEUDOTYPE(GEOMETRY) -> PSEUDOTYPE(GEOMETRY)
+
+ // Constants for specifying key ranges
+ MINVAL = 180;
+ MAXVAL = 181;
+ }
+ optional TermType type = 1;
+
+ // This is only used when type is DATUM.
+ optional Datum datum = 2;
+
+ repeated Term args = 3; // Holds the positional arguments of the query.
+ message AssocPair {
+ optional string key = 1;
+ optional Term val = 2;
+ }
+ repeated AssocPair optargs = 4; // Holds the optional arguments of the query.
+ // (Note that the order of the optional arguments doesn't matter; think of a
+ // Hash.)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// EXAMPLE //
+////////////////////////////////////////////////////////////////////////////////
+// ```ruby
+// r.table('tbl', {:read_mode => 'outdated'}).insert([{:id => 0}, {:id => 1}])
+// ```
+// Would turn into:
+// Term {
+// type = INSERT;
+// args = [Term {
+// type = TABLE;
+// args = [Term {
+// type = DATUM;
+// datum = Datum { type = R_STR; r_str = "tbl"; };
+// }];
+// optargs = [["read_mode",
+// Term {
+// type = DATUM;
+// datum = Datum { type = R_STR; r_bool = "outdated"; };
+// }]];
+// },
+// Term {
+// type = MAKE_ARRAY;
+// args = [Term {
+// type = DATUM;
+// datum = Datum { type = R_OBJECT; r_object = [["id", 0]]; };
+// },
+// Term {
+// type = DATUM;
+// datum = Datum { type = R_OBJECT; r_object = [["id", 1]]; };
+// }];
+// }]
+// }
+// And the server would reply:
+// Response {
+// type = SUCCESS_ATOM;
+// token = 1;
+// response = [Datum { type = R_OBJECT; r_object = [["inserted", 2]]; }];
+// }
+// Or, if there were an error:
+// Response {
+// type = RUNTIME_ERROR;
+// token = 1;
+// response = [Datum { type = R_STR; r_str = "The table `tbl` doesn't exist!"; }];
+// backtrace = [Frame { type = POS; pos = 0; }, Frame { type = POS; pos = 0; }];
+// }
diff --git a/ext/librethinkdbxx/src/connection.cc b/ext/librethinkdbxx/src/connection.cc
new file mode 100644
index 00000000..53d106ec
--- /dev/null
+++ b/ext/librethinkdbxx/src/connection.cc
@@ -0,0 +1,434 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+
+#include <netdb.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <cstring>
+#include <cinttypes>
+#include <memory>
+
+#include "connection.h"
+#include "connection_p.h"
+#include "json_p.h"
+#include "exceptions.h"
+#include "term.h"
+#include "cursor_p.h"
+
+#include "rapidjson-config.h"
+#include "rapidjson/rapidjson.h"
+#include "rapidjson/encodedstream.h"
+#include "rapidjson/document.h"
+
+namespace RethinkDB {
+
+using QueryType = Protocol::Query::QueryType;
+
+// constants
+const int debug_net = 0;
+const uint32_t version_magic =
+ static_cast<uint32_t>(Protocol::VersionDummy::Version::V0_4);
+const uint32_t json_magic =
+ static_cast<uint32_t>(Protocol::VersionDummy::Protocol::JSON);
+
+std::unique_ptr<Connection> connect(std::string host, int port, std::string auth_key) {
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ char port_str[16];
+ snprintf(port_str, 16, "%d", port);
+ struct addrinfo *servinfo;
+ int ret = getaddrinfo(host.c_str(), port_str, &hints, &servinfo);
+ if (ret) throw Error("getaddrinfo: %s\n", gai_strerror(ret));
+
+ struct addrinfo *p;
+ Error error;
+ int sockfd;
+ for (p = servinfo; p != NULL; p = p->ai_next) {
+ sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
+ if (sockfd == -1) {
+ error = Error::from_errno("socket");
+ continue;
+ }
+
+ if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
+ ::close(sockfd);
+ error = Error::from_errno("connect");
+ continue;
+ }
+
+ break;
+ }
+
+ if (p == NULL) {
+ throw error;
+ }
+
+ freeaddrinfo(servinfo);
+
+ std::unique_ptr<ConnectionPrivate> conn_private(new ConnectionPrivate(sockfd));
+ WriteLock writer(conn_private.get());
+ {
+ size_t size = auth_key.size();
+ char buf[12 + size];
+ memcpy(buf, &version_magic, 4);
+ uint32_t n = size;
+ memcpy(buf + 4, &n, 4);
+ memcpy(buf + 8, auth_key.data(), size);
+ memcpy(buf + 8 + size, &json_magic, 4);
+ writer.send(buf, sizeof buf);
+ }
+
+ ReadLock reader(conn_private.get());
+ {
+ const size_t max_response_length = 1024;
+ char buf[max_response_length + 1];
+ size_t len = reader.recv_cstring(buf, max_response_length);
+ if (len == max_response_length || strcmp(buf, "SUCCESS")) {
+ buf[len] = 0;
+ ::close(sockfd);
+ throw Error("Server rejected connection with message: %s", buf);
+ }
+ }
+
+ return std::unique_ptr<Connection>(new Connection(conn_private.release()));
+}
+
+Connection::Connection(ConnectionPrivate *dd) : d(dd) { }
+Connection::~Connection() {
+ // close();
+ if (d->guarded_sockfd >= 0)
+ ::close(d->guarded_sockfd);
+}
+
+size_t ReadLock::recv_some(char* buf, size_t size, double wait) {
+ if (wait != FOREVER) {
+ while (true) {
+ fd_set readfds;
+ struct timeval tv;
+
+ FD_ZERO(&readfds);
+ FD_SET(conn->guarded_sockfd, &readfds);
+
+ tv.tv_sec = (int)wait;
+ tv.tv_usec = (int)((wait - (int)wait) / MICROSECOND);
+ int rv = select(conn->guarded_sockfd + 1, &readfds, NULL, NULL, &tv);
+ if (rv == -1) {
+ throw Error::from_errno("select");
+ } else if (rv == 0) {
+ throw TimeoutException();
+ }
+
+ if (FD_ISSET(conn->guarded_sockfd, &readfds)) {
+ break;
+ }
+ }
+ }
+
+ ssize_t numbytes = ::recv(conn->guarded_sockfd, buf, size, 0);
+ if (numbytes <= 0) throw Error::from_errno("recv");
+ if (debug_net > 1) {
+ fprintf(stderr, "<< %s\n", write_datum(std::string(buf, numbytes)).c_str());
+ }
+
+ return numbytes;
+}
+
+void ReadLock::recv(char* buf, size_t size, double wait) {
+ while (size) {
+ size_t numbytes = recv_some(buf, size, wait);
+
+ buf += numbytes;
+ size -= numbytes;
+ }
+}
+
+size_t ReadLock::recv_cstring(char* buf, size_t max_size){
+ size_t size = 0;
+ for (; size < max_size; size++) {
+ recv(buf, 1, FOREVER);
+ if (*buf == 0) {
+ break;
+ }
+ buf++;
+ }
+ return size;
+}
+
+void WriteLock::send(const char* buf, size_t size) {
+ while (size) {
+ ssize_t numbytes = ::write(conn->guarded_sockfd, buf, size);
+ if (numbytes == -1) throw Error::from_errno("write");
+ if (debug_net > 1) {
+ fprintf(stderr, ">> %s\n", write_datum(std::string(buf, numbytes)).c_str());
+ }
+
+ buf += numbytes;
+ size -= numbytes;
+ }
+}
+
+void WriteLock::send(const std::string data) {
+ send(data.data(), data.size());
+}
+
+std::string ReadLock::recv(size_t size) {
+ char buf[size];
+ recv(buf, size, FOREVER);
+ return buf;
+}
+
+void Connection::close() {
+ CacheLock guard(d.get());
+ for (auto& it : d->guarded_cache) {
+ stop_query(it.first);
+ }
+
+ int ret = ::close(d->guarded_sockfd);
+ if (ret == -1) {
+ throw Error::from_errno("close");
+ }
+ d->guarded_sockfd = -1;
+}
+
+Response ConnectionPrivate::wait_for_response(uint64_t token_want, double wait) {
+ CacheLock guard(this);
+ ConnectionPrivate::TokenCache& cache = guarded_cache[token_want];
+
+ while (true) {
+ if (!cache.responses.empty()) {
+ Response response(std::move(cache.responses.front()));
+ cache.responses.pop();
+ if (cache.closed && cache.responses.empty()) {
+ guarded_cache.erase(token_want);
+ }
+
+ return response;
+ }
+
+ if (cache.closed) {
+ throw Error("Trying to read from a closed token");
+ }
+
+ if (guarded_loop_active) {
+ cache.cond.wait(guard.inner_lock);
+ } else {
+ break;
+ }
+ }
+
+ ReadLock reader(this);
+ return reader.read_loop(token_want, std::move(guard), wait);
+}
+
+Response ReadLock::read_loop(uint64_t token_want, CacheLock&& guard, double wait) {
+ if (!guard.inner_lock) {
+ guard.lock();
+ }
+ if (conn->guarded_loop_active) {
+ throw Error("Cannot run more than one read loop on the same connection");
+ }
+ conn->guarded_loop_active = true;
+ guard.unlock();
+
+ try {
+ while (true) {
+ char buf[12];
+ bzero(buf, sizeof(buf));
+ recv(buf, 12, wait);
+ uint64_t token_got;
+ memcpy(&token_got, buf, 8);
+ uint32_t length;
+ memcpy(&length, buf + 8, 4);
+
+ std::unique_ptr<char[]> bufmem(new char[length + 1]);
+ char *buffer = bufmem.get();
+ bzero(buffer, length + 1);
+ recv(buffer, length, wait);
+ buffer[length] = '\0';
+
+ rapidjson::Document json;
+ json.ParseInsitu(buffer);
+ if (json.HasParseError()) {
+ fprintf(stderr, "json parse error, code: %d, position: %d\n",
+ (int)json.GetParseError(), (int)json.GetErrorOffset());
+ } else if (json.IsNull()) {
+ fprintf(stderr, "null value, read: %s\n", buffer);
+ }
+
+ Datum datum = read_datum(json);
+ if (debug_net > 0) {
+ fprintf(stderr, "[%" PRIu64 "] << %s\n", token_got, write_datum(datum).c_str());
+ }
+
+ Response response(std::move(datum));
+
+ if (token_got == token_want) {
+ guard.lock();
+ if (response.type != Protocol::Response::ResponseType::SUCCESS_PARTIAL) {
+ auto it = conn->guarded_cache.find(token_got);
+ if (it != conn->guarded_cache.end()) {
+ it->second.closed = true;
+ it->second.cond.notify_all();
+ }
+ conn->guarded_cache.erase(it);
+ }
+ conn->guarded_loop_active = false;
+ for (auto& it : conn->guarded_cache) {
+ it.second.cond.notify_all();
+ }
+ return response;
+ } else {
+ guard.lock();
+ auto it = conn->guarded_cache.find(token_got);
+ if (it == conn->guarded_cache.end()) {
+ // drop the response
+ } else if (!it->second.closed) {
+ it->second.responses.emplace(std::move(response));
+ if (response.type != Protocol::Response::ResponseType::SUCCESS_PARTIAL) {
+ it->second.closed = true;
+ }
+ }
+ it->second.cond.notify_all();
+ guard.unlock();
+ }
+ }
+ } catch (const TimeoutException &e) {
+ if (!guard.inner_lock){
+ guard.lock();
+ }
+ conn->guarded_loop_active = false;
+ throw e;
+ }
+}
+
+void ConnectionPrivate::run_query(Query query, bool no_reply) {
+ WriteLock writer(this);
+ writer.send(query.serialize());
+}
+
+Cursor Connection::start_query(Term *term, OptArgs&& opts) {
+ bool no_reply = false;
+ auto it = opts.find("noreply");
+ if (it != opts.end()) {
+ no_reply = *(it->second.datum.get_boolean());
+ }
+
+ uint64_t token = d->new_token();
+ {
+ CacheLock guard(d.get());
+ d->guarded_cache[token];
+ }
+
+ d->run_query(Query{QueryType::START, token, term->datum, std::move(opts)});
+ if (no_reply) {
+ return Cursor(new CursorPrivate(token, this, Nil()));
+ }
+
+ Cursor cursor(new CursorPrivate(token, this));
+ Response response = d->wait_for_response(token, FOREVER);
+ cursor.d->add_response(std::move(response));
+ return cursor;
+}
+
+void Connection::stop_query(uint64_t token) {
+ const auto& it = d->guarded_cache.find(token);
+ if (it != d->guarded_cache.end() && !it->second.closed) {
+ d->run_query(Query{QueryType::STOP, token}, true);
+ }
+}
+
+void Connection::continue_query(uint64_t token) {
+ d->run_query(Query{QueryType::CONTINUE, token}, true);
+}
+
+Error Response::as_error() {
+ std::string repr;
+ if (result.size() == 1) {
+ std::string* string = result[0].get_string();
+ if (string) {
+ repr = *string;
+ } else {
+ repr = write_datum(result[0]);
+ }
+ } else {
+ repr = write_datum(Datum(result));
+ }
+ std::string err;
+ using RT = Protocol::Response::ResponseType;
+ using ET = Protocol::Response::ErrorType;
+ switch (type) {
+ case RT::SUCCESS_SEQUENCE: err = "unexpected response: SUCCESS_SEQUENCE"; break;
+ case RT::SUCCESS_PARTIAL: err = "unexpected response: SUCCESS_PARTIAL"; break;
+ case RT::SUCCESS_ATOM: err = "unexpected response: SUCCESS_ATOM"; break;
+ case RT::WAIT_COMPLETE: err = "unexpected response: WAIT_COMPLETE"; break;
+ case RT::SERVER_INFO: err = "unexpected response: SERVER_INFO"; break;
+ case RT::CLIENT_ERROR: err = "ReqlDriverError"; break;
+ case RT::COMPILE_ERROR: err = "ReqlCompileError"; break;
+ case RT::RUNTIME_ERROR:
+ switch (error_type) {
+ case ET::INTERNAL: err = "ReqlInternalError"; break;
+ case ET::RESOURCE_LIMIT: err = "ReqlResourceLimitError"; break;
+ case ET::QUERY_LOGIC: err = "ReqlQueryLogicError"; break;
+ case ET::NON_EXISTENCE: err = "ReqlNonExistenceError"; break;
+ case ET::OP_FAILED: err = "ReqlOpFailedError"; break;
+ case ET::OP_INDETERMINATE: err = "ReqlOpIndeterminateError"; break;
+ case ET::USER: err = "ReqlUserError"; break;
+ case ET::PERMISSION_ERROR: err = "ReqlPermissionError"; break;
+ default: err = "ReqlRuntimeError"; break;
+ }
+ }
+ throw Error("%s: %s", err.c_str(), repr.c_str());
+}
+
+Protocol::Response::ResponseType response_type(double t) {
+ int n = static_cast<int>(t);
+ using RT = Protocol::Response::ResponseType;
+ switch (n) {
+ case static_cast<int>(RT::SUCCESS_ATOM):
+ return RT::SUCCESS_ATOM;
+ case static_cast<int>(RT::SUCCESS_SEQUENCE):
+ return RT::SUCCESS_SEQUENCE;
+ case static_cast<int>(RT::SUCCESS_PARTIAL):
+ return RT::SUCCESS_PARTIAL;
+ case static_cast<int>(RT::WAIT_COMPLETE):
+ return RT::WAIT_COMPLETE;
+ case static_cast<int>(RT::CLIENT_ERROR):
+ return RT::CLIENT_ERROR;
+ case static_cast<int>(RT::COMPILE_ERROR):
+ return RT::COMPILE_ERROR;
+ case static_cast<int>(RT::RUNTIME_ERROR):
+ return RT::RUNTIME_ERROR;
+ default:
+ throw Error("Unknown response type");
+ }
+}
+
+Protocol::Response::ErrorType runtime_error_type(double t) {
+ int n = static_cast<int>(t);
+ using ET = Protocol::Response::ErrorType;
+ switch (n) {
+ case static_cast<int>(ET::INTERNAL):
+ return ET::INTERNAL;
+ case static_cast<int>(ET::RESOURCE_LIMIT):
+ return ET::RESOURCE_LIMIT;
+ case static_cast<int>(ET::QUERY_LOGIC):
+ return ET::QUERY_LOGIC;
+ case static_cast<int>(ET::NON_EXISTENCE):
+ return ET::NON_EXISTENCE;
+ case static_cast<int>(ET::OP_FAILED):
+ return ET::OP_FAILED;
+ case static_cast<int>(ET::OP_INDETERMINATE):
+ return ET::OP_INDETERMINATE;
+ case static_cast<int>(ET::USER):
+ return ET::USER;
+ default:
+ throw Error("Unknown error type");
+ }
+}
+
+}
diff --git a/ext/librethinkdbxx/src/connection.h b/ext/librethinkdbxx/src/connection.h
new file mode 100644
index 00000000..d3882857
--- /dev/null
+++ b/ext/librethinkdbxx/src/connection.h
@@ -0,0 +1,59 @@
+#pragma once
+
+#include <string>
+#include <queue>
+#include <mutex>
+#include <memory>
+#include <condition_variable>
+
+#include "protocol_defs.h"
+#include "datum.h"
+#include "error.h"
+
+#define FOREVER (-1)
+#define SECOND 1
+#define MICROSECOND 0.000001
+
+namespace RethinkDB {
+
+class Term;
+using OptArgs = std::map<std::string, Term>;
+
+// A connection to a RethinkDB server
+// It contains:
+// * A socket
+// * Read and write locks
+// * A cache of responses that have not been read by the corresponding Cursor
+class ConnectionPrivate;
+class Connection {
+public:
+ Connection() = delete;
+ Connection(const Connection&) noexcept = delete;
+ Connection(Connection&&) noexcept = delete;
+ Connection& operator=(Connection&&) noexcept = delete;
+ Connection& operator=(const Connection&) noexcept = delete;
+ ~Connection();
+
+ void close();
+
+private:
+ explicit Connection(ConnectionPrivate *dd);
+ std::unique_ptr<ConnectionPrivate> d;
+
+ Cursor start_query(Term *term, OptArgs&& args);
+ void stop_query(uint64_t);
+ void continue_query(uint64_t);
+
+ friend class Cursor;
+ friend class CursorPrivate;
+ friend class Token;
+ friend class Term;
+ friend std::unique_ptr<Connection>
+ connect(std::string host, int port, std::string auth_key);
+
+};
+
+// $doc(connect)
+std::unique_ptr<Connection> connect(std::string host = "localhost", int port = 28015, std::string auth_key = "");
+
+}
diff --git a/ext/librethinkdbxx/src/connection_p.h b/ext/librethinkdbxx/src/connection_p.h
new file mode 100644
index 00000000..d8a95e3c
--- /dev/null
+++ b/ext/librethinkdbxx/src/connection_p.h
@@ -0,0 +1,133 @@
+#ifndef CONNECTION_P_H
+#define CONNECTION_P_H
+
+#include <inttypes.h>
+
+#include "connection.h"
+#include "term.h"
+#include "json_p.h"
+
+namespace RethinkDB {
+
+extern const int debug_net;
+
+struct Query {
+ Protocol::Query::QueryType type;
+ uint64_t token;
+ Datum term;
+ OptArgs optArgs;
+
+ std::string serialize() {
+ Array query_arr{static_cast<double>(type)};
+ if (term.is_valid()) query_arr.emplace_back(term);
+ if (!optArgs.empty())
+ query_arr.emplace_back(Term(std::move(optArgs)).datum);
+
+ std::string query_str = write_datum(query_arr);
+ if (debug_net > 0) {
+ fprintf(stderr, "[%" PRIu64 "] >> %s\n", token, query_str.c_str());
+ }
+
+ char header[12];
+ memcpy(header, &token, 8);
+ uint32_t size = query_str.size();
+ memcpy(header + 8, &size, 4);
+ query_str.insert(0, header, 12);
+ return query_str;
+ }
+};
+
+// Used internally to convert a raw response type into an enum
+Protocol::Response::ResponseType response_type(double t);
+Protocol::Response::ErrorType runtime_error_type(double t);
+
+// Contains a response from the server. Use the Cursor class to interact with these responses
+class Response {
+public:
+ Response() = delete;
+ explicit Response(Datum&& datum) :
+ type(response_type(std::move(datum).extract_field("t").extract_number())),
+ error_type(datum.get_field("e") ?
+ runtime_error_type(std::move(datum).extract_field("e").extract_number()) :
+ Protocol::Response::ErrorType(0)),
+ result(std::move(datum).extract_field("r").extract_array()) { }
+ Error as_error();
+ Protocol::Response::ResponseType type;
+ Protocol::Response::ErrorType error_type;
+ Array result;
+};
+
+class Token;
+class ConnectionPrivate {
+public:
+ ConnectionPrivate(int sockfd)
+ : guarded_next_token(1), guarded_sockfd(sockfd), guarded_loop_active(false)
+ { }
+
+ void run_query(Query query, bool no_reply = false);
+
+ Response wait_for_response(uint64_t, double);
+ uint64_t new_token() {
+ return guarded_next_token++;
+ }
+
+ std::mutex read_lock;
+ std::mutex write_lock;
+ std::mutex cache_lock;
+
+ struct TokenCache {
+ bool closed = false;
+ std::condition_variable cond;
+ std::queue<Response> responses;
+ };
+
+ std::map<uint64_t, TokenCache> guarded_cache;
+ uint64_t guarded_next_token;
+ int guarded_sockfd;
+ bool guarded_loop_active;
+};
+
+class CacheLock {
+public:
+ CacheLock(ConnectionPrivate* conn) : inner_lock(conn->cache_lock) { }
+
+ void lock() {
+ inner_lock.lock();
+ }
+
+ void unlock() {
+ inner_lock.unlock();
+ }
+
+ std::unique_lock<std::mutex> inner_lock;
+};
+
+class ReadLock {
+public:
+ ReadLock(ConnectionPrivate* conn_) : lock(conn_->read_lock), conn(conn_) { }
+
+ size_t recv_some(char*, size_t, double wait);
+ void recv(char*, size_t, double wait);
+ std::string recv(size_t);
+ size_t recv_cstring(char*, size_t);
+
+ Response read_loop(uint64_t, CacheLock&&, double);
+
+ std::lock_guard<std::mutex> lock;
+ ConnectionPrivate* conn;
+};
+
+class WriteLock {
+public:
+ WriteLock(ConnectionPrivate* conn_) : lock(conn_->write_lock), conn(conn_) { }
+
+ void send(const char*, size_t);
+ void send(std::string);
+
+ std::lock_guard<std::mutex> lock;
+ ConnectionPrivate* conn;
+};
+
+} // namespace RethinkDB
+
+#endif // CONNECTION_P_H
diff --git a/ext/librethinkdbxx/src/cursor.cc b/ext/librethinkdbxx/src/cursor.cc
new file mode 100644
index 00000000..df0621eb
--- /dev/null
+++ b/ext/librethinkdbxx/src/cursor.cc
@@ -0,0 +1,223 @@
+#include "cursor.h"
+#include "cursor_p.h"
+#include "exceptions.h"
+
+namespace RethinkDB {
+
+// for type completion, in order to forward declare with unique_ptr
+Cursor::Cursor(Cursor&&) = default;
+Cursor& Cursor::operator=(Cursor&&) = default;
+
+CursorPrivate::CursorPrivate(uint64_t token_, Connection *conn_)
+ : single(false), no_more(false), index(0),
+ token(token_), conn(conn_)
+{ }
+
+CursorPrivate::CursorPrivate(uint64_t token_, Connection *conn_, Datum&& datum)
+ : single(true), no_more(true), index(0), buffer(Array{std::move(datum)}),
+ token(token_), conn(conn_)
+{ }
+
+Cursor::Cursor(CursorPrivate *dd) : d(dd) {}
+
+Cursor::~Cursor() {
+ try {
+ if (d && d->conn) {
+ close();
+ }
+ } catch ( ... ) {}
+}
+
+Datum& Cursor::next(double wait) const {
+ if (!has_next(wait)) {
+ throw Error("next: No more data");
+ }
+
+ return d->buffer[d->index++];
+}
+
+Datum& Cursor::peek(double wait) const {
+ if (!has_next(wait)) {
+ throw Error("next: No more data");
+ }
+
+ return d->buffer[d->index];
+}
+
+void Cursor::each(std::function<void(Datum&&)> f, double wait) const {
+ while (has_next(wait)) {
+ f(std::move(d->buffer[d->index++]));
+ }
+}
+
+void CursorPrivate::convert_single() const {
+ if (index != 0) {
+ throw Error("Cursor: already consumed");
+ }
+
+ if (buffer.size() != 1) {
+ throw Error("Cursor: invalid response from server");
+ }
+
+ if (!buffer[0].is_array()) {
+ throw Error("Cursor: not an array");
+ }
+
+ buffer.swap(buffer[0].extract_array());
+ single = false;
+}
+
+void CursorPrivate::clear_and_read_all() const {
+ if (single) {
+ convert_single();
+ }
+ if (index != 0) {
+ buffer.erase(buffer.begin(), buffer.begin() + index);
+ index = 0;
+ }
+ while (!no_more) {
+ add_response(conn->d->wait_for_response(token, FOREVER));
+ }
+}
+
+Array&& Cursor::to_array() && {
+ d->clear_and_read_all();
+ return std::move(d->buffer);
+}
+
+Array Cursor::to_array() const & {
+ d->clear_and_read_all();
+ return d->buffer;
+}
+
+Datum Cursor::to_datum() const & {
+ if (d->single) {
+ if (d->index != 0) {
+ throw Error("to_datum: already consumed");
+ }
+ return d->buffer[0];
+ }
+
+ d->clear_and_read_all();
+ return d->buffer;
+}
+
+Datum Cursor::to_datum() && {
+ Datum ret((Nil()));
+ if (d->single) {
+ if (d->index != 0) {
+ throw Error("to_datum: already consumed");
+ }
+ ret = std::move(d->buffer[0]);
+ } else {
+ d->clear_and_read_all();
+ ret = std::move(d->buffer);
+ }
+
+ return ret;
+}
+
+void Cursor::close() const {
+ d->conn->stop_query(d->token);
+ d->no_more = true;
+}
+
+bool Cursor::has_next(double wait) const {
+ if (d->single) {
+ d->convert_single();
+ }
+
+ while (true) {
+ if (d->index >= d->buffer.size()) {
+ if (d->no_more) {
+ return false;
+ }
+ d->add_response(d->conn->d->wait_for_response(d->token, wait));
+ } else {
+ return true;
+ }
+ }
+}
+
+bool Cursor::is_single() const {
+ return d->single;
+}
+
+void CursorPrivate::add_results(Array&& results) const {
+ if (index >= buffer.size()) {
+ buffer = std::move(results);
+ index = 0;
+ } else {
+ for (auto& it : results) {
+ buffer.emplace_back(std::move(it));
+ }
+ }
+}
+
+void CursorPrivate::add_response(Response&& response) const {
+ using RT = Protocol::Response::ResponseType;
+ switch (response.type) {
+ case RT::SUCCESS_SEQUENCE:
+ add_results(std::move(response.result));
+ no_more = true;
+ break;
+ case RT::SUCCESS_PARTIAL:
+ conn->continue_query(token);
+ add_results(std::move(response.result));
+ break;
+ case RT::SUCCESS_ATOM:
+ add_results(std::move(response.result));
+ single = true;
+ no_more = true;
+ break;
+ case RT::SERVER_INFO:
+ add_results(std::move(response.result));
+ single = true;
+ no_more = true;
+ break;
+ case RT::WAIT_COMPLETE:
+ case RT::CLIENT_ERROR:
+ case RT::COMPILE_ERROR:
+ case RT::RUNTIME_ERROR:
+ no_more = true;
+ throw response.as_error();
+ }
+}
+
+Cursor::iterator Cursor::begin() {
+ return iterator(this);
+}
+
+Cursor::iterator Cursor::end() {
+ return iterator(nullptr);
+}
+
+Cursor::iterator::iterator(Cursor* cursor_) : cursor(cursor_) {}
+
+Cursor::iterator& Cursor::iterator::operator++ () {
+ if (cursor == nullptr) {
+ throw Error("incrementing an exhausted Cursor iterator");
+ }
+
+ cursor->next();
+ return *this;
+}
+
+Datum& Cursor::iterator::operator* () {
+ if (cursor == nullptr) {
+ throw Error("reading from empty Cursor iterator");
+ }
+
+ return cursor->peek();
+}
+
+bool Cursor::iterator::operator!= (const Cursor::iterator& other) const {
+ if (cursor == other.cursor) {
+ return false;
+ }
+
+ return !((cursor == nullptr && !other.cursor->has_next()) ||
+ (other.cursor == nullptr && !cursor->has_next()));
+}
+
+}
diff --git a/ext/librethinkdbxx/src/cursor.h b/ext/librethinkdbxx/src/cursor.h
new file mode 100644
index 00000000..60ae1817
--- /dev/null
+++ b/ext/librethinkdbxx/src/cursor.h
@@ -0,0 +1,76 @@
+#pragma once
+
+#include "connection.h"
+
+namespace RethinkDB {
+
+// The response from the server, as returned by run.
+// The response is either a single datum or a stream:
+// * If it is a stream, the cursor represents each element of the stream.
+// - Batches are fetched from the server as needed.
+// * If it is a single datum, is_single() returns true.
+// - If it is an array, the cursor represents each element of that array
+// - Otherwise, to_datum() returns the datum and iteration throws an exception.
+// The cursor can only be iterated over once, it discards data that has already been read.
+class CursorPrivate;
+class Cursor {
+public:
+ Cursor() = delete;
+ ~Cursor();
+
+ Cursor(Cursor&&); // movable
+ Cursor& operator=(Cursor&&);
+ Cursor(const Cursor&) = delete; // not copyable
+ Cursor& operator=(const Cursor&) = delete;
+
+ // Returned by begin() and end()
+ class iterator {
+ public:
+ iterator(Cursor*);
+ iterator& operator++ ();
+ Datum& operator* ();
+ bool operator!= (const iterator&) const;
+
+ private:
+ Cursor *cursor;
+ };
+
+ // Consume the next element
+ Datum& next(double wait = FOREVER) const;
+
+ // Peek at the next element
+ Datum& peek(double wait = FOREVER) const;
+
+ // Call f on every element of the Cursor
+ void each(std::function<void(Datum&&)> f, double wait = FOREVER) const;
+
+ // Consume and return all elements
+ Array&& to_array() &&;
+
+ // If is_single(), returns the single datum. Otherwise returns to_array().
+ Datum to_datum() &&;
+ Datum to_datum() const &;
+
+ // Efficiently consume and return all elements
+ Array to_array() const &;
+
+ // Close the cursor
+ void close() const;
+
+ // Returns false if there are no more elements
+ bool has_next(double wait = FOREVER) const;
+
+ // Returns false if the cursor is a stream
+ bool is_single() const;
+
+ iterator begin();
+ iterator end();
+
+private:
+ explicit Cursor(CursorPrivate *dd);
+ std::unique_ptr<CursorPrivate> d;
+
+ friend class Connection;
+};
+
+}
diff --git a/ext/librethinkdbxx/src/cursor_p.h b/ext/librethinkdbxx/src/cursor_p.h
new file mode 100644
index 00000000..ce584cd7
--- /dev/null
+++ b/ext/librethinkdbxx/src/cursor_p.h
@@ -0,0 +1,29 @@
+#ifndef CURSOR_P_H
+#define CURSOR_P_H
+
+#include "connection_p.h"
+
+namespace RethinkDB {
+
+class CursorPrivate {
+public:
+ CursorPrivate(uint64_t token, Connection *conn);
+ CursorPrivate(uint64_t token, Connection *conn, Datum&&);
+
+ void add_response(Response&&) const;
+ void add_results(Array&&) const;
+ void clear_and_read_all() const;
+ void convert_single() const;
+
+ mutable bool single = false;
+ mutable bool no_more = false;
+ mutable size_t index = 0;
+ mutable Array buffer;
+
+ uint64_t token;
+ Connection *conn;
+};
+
+} // namespace RethinkDB
+
+#endif // CURSOR_P_H \ No newline at end of file
diff --git a/ext/librethinkdbxx/src/datum.cc b/ext/librethinkdbxx/src/datum.cc
new file mode 100644
index 00000000..e4dbc8dc
--- /dev/null
+++ b/ext/librethinkdbxx/src/datum.cc
@@ -0,0 +1,449 @@
+#include <float.h>
+#include <cmath>
+
+#include "datum.h"
+#include "json_p.h"
+#include "utils.h"
+#include "cursor.h"
+
+#include "rapidjson-config.h"
+#include "rapidjson/prettywriter.h"
+#include "rapidjson/stringbuffer.h"
+
+namespace RethinkDB {
+
+using TT = Protocol::Term::TermType;
+
+bool Datum::is_nil() const {
+ return type == Type::NIL;
+}
+
+bool Datum::is_boolean() const {
+ return type == Type::BOOLEAN;
+}
+
+bool Datum::is_number() const {
+ return type == Type::NUMBER;
+}
+
+bool Datum::is_string() const {
+ return type == Type::STRING;
+}
+
+bool Datum::is_object() const {
+ return type == Type::OBJECT;
+}
+
+bool Datum::is_array() const {
+ return type == Type::ARRAY;
+}
+
+bool Datum::is_binary() const {
+ return type == Type::BINARY;
+}
+
+bool Datum::is_time() const {
+ return type == Type::TIME;
+}
+
+bool* Datum::get_boolean() {
+ if (type == Type::BOOLEAN) {
+ return &value.boolean;
+ } else {
+ return NULL;
+ }
+}
+
+const bool* Datum::get_boolean() const {
+ if (type == Type::BOOLEAN) {
+ return &value.boolean;
+ } else {
+ return NULL;
+ }
+}
+
+double* Datum::get_number() {
+ if (type == Type::NUMBER) {
+ return &value.number;
+ } else {
+ return NULL;
+ }
+}
+
+const double* Datum::get_number() const {
+ if (type == Type::NUMBER) {
+ return &value.number;
+ } else {
+ return NULL;
+ }
+}
+
+std::string* Datum::get_string() {
+ if (type == Type::STRING) {
+ return &value.string;
+ } else {
+ return NULL;
+ }
+}
+
+const std::string* Datum::get_string() const {
+ if (type == Type::STRING) {
+ return &value.string;
+ } else {
+ return NULL;
+ }
+}
+
+Datum* Datum::get_field(std::string key) {
+ if (type != Type::OBJECT) {
+ return NULL;
+ }
+ auto it = value.object.find(key);
+ if (it == value.object.end()) {
+ return NULL;
+ }
+ return &it->second;
+}
+
+const Datum* Datum::get_field(std::string key) const {
+ if (type != Type::OBJECT) {
+ return NULL;
+ }
+ auto it = value.object.find(key);
+ if (it == value.object.end()) {
+ return NULL;
+ }
+ return &it->second;
+}
+
+Datum* Datum::get_nth(size_t i) {
+ if (type != Type::ARRAY) {
+ return NULL;
+ }
+ if (i >= value.array.size()) {
+ return NULL;
+ }
+ return &value.array[i];
+}
+
+const Datum* Datum::get_nth(size_t i) const {
+ if (type != Type::ARRAY) {
+ return NULL;
+ }
+ if (i >= value.array.size()) {
+ return NULL;
+ }
+ return &value.array[i];
+}
+
+Object* Datum::get_object() {
+ if (type == Type::OBJECT) {
+ return &value.object;
+ } else {
+ return NULL;
+ }
+}
+
+const Object* Datum::get_object() const {
+ if (type == Type::OBJECT) {
+ return &value.object;
+ } else {
+ return NULL;
+ }
+}
+
+Array* Datum::get_array() {
+ if (type == Type::ARRAY) {
+ return &value.array;
+ } else {
+ return NULL;
+ }
+}
+
+const Array* Datum::get_array() const {
+ if (type == Type::ARRAY) {
+ return &value.array;
+ } else {
+ return NULL;
+ }
+}
+
+Binary* Datum::get_binary() {
+ if (type == Type::BINARY) {
+ return &value.binary;
+ } else {
+ return NULL;
+ }
+}
+
+const Binary* Datum::get_binary() const {
+ if (type == Type::BINARY) {
+ return &value.binary;
+ } else {
+ return NULL;
+ }
+}
+
+Time* Datum::get_time() {
+ if (type == Type::TIME) {
+ return &value.time;
+ } else {
+ return NULL;
+ }
+}
+
+const Time* Datum::get_time() const {
+ if (type == Type::TIME) {
+ return &value.time;
+ } else {
+ return NULL;
+ }
+}
+
+bool& Datum::extract_boolean() {
+ if (type != Type::BOOLEAN) {
+ throw Error("extract_bool: Not a boolean");
+ }
+ return value.boolean;
+}
+
+double& Datum::extract_number() {
+ if (type != Type::NUMBER) {
+ throw Error("extract_number: Not a number: %s", write_datum(*this).c_str());
+ }
+ return value.number;
+}
+
+std::string& Datum::extract_string() {
+ if (type != Type::STRING) {
+ throw Error("extract_string: Not a string");
+ }
+ return value.string;
+}
+
+Object& Datum::extract_object() {
+ if (type != Type::OBJECT) {
+ throw Error("extract_object: Not an object");
+ }
+ return value.object;
+}
+
+Datum& Datum::extract_field(std::string key) {
+ if (type != Type::OBJECT) {
+ throw Error("extract_field: Not an object");
+ }
+ auto it = value.object.find(key);
+ if (it == value.object.end()) {
+ throw Error("extract_field: No such key in object");
+ }
+ return it->second;
+}
+
+Datum& Datum::extract_nth(size_t i) {
+ if (type != Type::ARRAY) {
+ throw Error("extract_nth: Not an array");
+ }
+ if (i >= value.array.size()) {
+ throw Error("extract_nth: index too large");
+ }
+ return value.array[i];
+}
+
+Array& Datum::extract_array() {
+ if (type != Type::ARRAY) {
+ throw Error("get_array: Not an array");
+ }
+ return value.array;
+}
+
+Binary& Datum::extract_binary() {
+ if (type != Type::BINARY) {
+ throw Error("get_binary: Not a binary");
+ }
+ return value.binary;
+}
+
+Time& Datum::extract_time() {
+ if (type != Type::TIME) {
+ throw Error("get_time: Not a time");
+ }
+ return value.time;
+}
+
+int Datum::compare(const Datum& other) const {
+#define COMPARE(a, b) do { \
+ if (a < b) { return -1; } \
+ if (a > b) { return 1; } } while(0)
+#define COMPARE_OTHER(x) COMPARE(x, other.x)
+
+ COMPARE_OTHER(type);
+ int c;
+ switch (type) {
+ case Type::NIL: case Type::INVALID: break;
+ case Type::BOOLEAN: COMPARE_OTHER(value.boolean); break;
+ case Type::NUMBER: COMPARE_OTHER(value.number); break;
+ case Type::STRING:
+ c = value.string.compare(other.value.string);
+ COMPARE(c, 0);
+ break;
+ case Type::BINARY:
+ c = value.binary.data.compare(other.value.binary.data);
+ COMPARE(c, 0);
+ break;
+ case Type::TIME:
+ COMPARE(value.time.epoch_time, other.value.time.epoch_time);
+ COMPARE(value.time.utc_offset, other.value.time.utc_offset);
+ break;
+ case Type::ARRAY:
+ COMPARE_OTHER(value.array.size());
+ for (size_t i = 0; i < value.array.size(); i++) {
+ c = value.array[i].compare(other.value.array[i]);
+ COMPARE(c, 0);
+ }
+ break;
+ case Type::OBJECT:
+ COMPARE_OTHER(value.object.size());
+ for (Object::const_iterator l = value.object.begin(),
+ r = other.value.object.begin();
+ l != value.object.end();
+ ++l, ++r) {
+ COMPARE(l->first, r->first);
+ c = l->second.compare(r->second);
+ COMPARE(c, 0);
+ }
+ break;
+ default:
+ throw Error("cannot compare invalid datum");
+ }
+ return 0;
+#undef COMPARE_OTHER
+#undef COMPARE
+}
+
+bool Datum::operator== (const Datum& other) const {
+ return compare(other) == 0;
+}
+
+Datum Datum::from_raw() const {
+ do {
+ const Datum* type_field = get_field("$reql_type$");
+ if (!type_field) break;
+ const std::string* type = type_field->get_string();
+ if (!type) break;;
+ if (!strcmp(type->c_str(), "BINARY")) {
+ const Datum* data_field = get_field("data");
+ if (!data_field) break;
+ const std::string* encoded_data = data_field->get_string();
+ if (!encoded_data) break;
+ Binary binary("");
+ if (base64_decode(*encoded_data, binary.data)) {
+ return binary;
+ }
+ } else if (!strcmp(type->c_str(), "TIME")) {
+ const Datum* epoch_field = get_field("epoch_time");
+ if (!epoch_field) break;
+ const Datum* tz_field = get_field("timezone");
+ if (!tz_field) break;
+ const double* epoch_time = epoch_field->get_number();
+ if (!epoch_time) break;
+ const std::string* tz = tz_field->get_string();
+ if (!tz) break;
+ double offset;
+ if (!Time::parse_utc_offset(*tz, &offset)) break;
+ return Time(*epoch_time, offset);
+ }
+ } while (0);
+ return *this;
+}
+
+Datum Datum::to_raw() const {
+ if (type == Type::BINARY) {
+ return Object{
+ {"$reql_type$", "BINARY"},
+ {"data", base64_encode(value.binary.data)}};
+ } else if (type == Type::TIME) {
+ return Object{
+ {"$reql_type$", "TIME"},
+ {"epoch_time", value.time.epoch_time},
+ {"timezone", Time::utc_offset_string(value.time.utc_offset)}};
+ }
+ return *this;
+}
+
+Datum::Datum(Cursor&& cursor) : Datum(cursor.to_datum()) { }
+Datum::Datum(const Cursor& cursor) : Datum(cursor.to_datum()) { }
+
+static const double max_dbl_int = 0x1LL << DBL_MANT_DIG;
+static const double min_dbl_int = max_dbl_int * -1;
+bool number_as_integer(double d, int64_t *i_out) {
+ static_assert(DBL_MANT_DIG == 53, "Doubles are wrong size.");
+
+ if (min_dbl_int <= d && d <= max_dbl_int) {
+ int64_t i = d;
+ if (static_cast<double>(i) == d) {
+ *i_out = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+template void Datum::write_json(
+ rapidjson::Writer<rapidjson::StringBuffer> *writer) const;
+template void Datum::write_json(
+ rapidjson::PrettyWriter<rapidjson::StringBuffer> *writer) const;
+
+template <class json_writer_t>
+void Datum::write_json(json_writer_t *writer) const {
+ switch (type) {
+ case Type::NIL: writer->Null(); break;
+ case Type::BOOLEAN: writer->Bool(value.boolean); break;
+ case Type::NUMBER: {
+ const double d = value.number;
+ // Always print -0.0 as a double since integers cannot represent -0.
+ // Otherwise check if the number is an integer and print it as such.
+ int64_t i;
+ if (!(d == 0.0 && std::signbit(d)) && number_as_integer(d, &i)) {
+ writer->Int64(i);
+ } else {
+ writer->Double(d);
+ }
+ } break;
+ case Type::STRING: writer->String(value.string.data(), value.string.size()); break;
+ case Type::ARRAY: {
+ writer->StartArray();
+ for (auto it : value.array) {
+ it.write_json(writer);
+ }
+ writer->EndArray();
+ } break;
+ case Type::OBJECT: {
+ writer->StartObject();
+ for (auto it : value.object) {
+ writer->Key(it.first.data(), it.first.size());
+ it.second.write_json(writer);
+ }
+ writer->EndObject();
+ } break;
+
+ case Type::BINARY:
+ case Type::TIME:
+ to_raw().write_json(writer);
+ break;
+ default:
+ throw Error("cannot write invalid datum");
+ }
+}
+
+std::string Datum::as_json() const {
+ rapidjson::StringBuffer buffer;
+ rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
+ write_json(&writer);
+ return std::string(buffer.GetString(), buffer.GetSize());
+}
+
+Datum Datum::from_json(const std::string& json) {
+ return read_datum(json);
+}
+
+} // namespace RethinkDB
diff --git a/ext/librethinkdbxx/src/datum.h b/ext/librethinkdbxx/src/datum.h
new file mode 100644
index 00000000..051e2ca2
--- /dev/null
+++ b/ext/librethinkdbxx/src/datum.h
@@ -0,0 +1,287 @@
+#pragma once
+
+#include <string>
+#include <vector>
+#include <map>
+#include <functional>
+
+#include "protocol_defs.h"
+#include "error.h"
+#include "types.h"
+
+namespace RethinkDB {
+
+class Cursor;
+
+// The type of data stored in a RethinkDB database.
+// The following JSON types are represented in a Datum as
+// * null -> Nil
+// * boolean -> bool
+// * number -> double
+// * unicode strings -> std::string
+// * array -> Array (aka std::vector<Datum>
+// * object -> Object (aka std::map<std::string, Datum>>
+// Datums can also contain one of the following extra types
+// * binary strings -> Binary
+// * timestamps -> Time
+// * points. lines and polygons -> not implemented
+class Datum {
+public:
+ Datum() : type(Type::INVALID), value() {}
+ Datum(Nil) : type(Type::NIL), value() { }
+ Datum(bool boolean_) : type(Type::BOOLEAN), value(boolean_) { }
+ Datum(double number_) : type(Type::NUMBER), value(number_) { }
+ Datum(const std::string& string_) : type(Type::STRING), value(string_) { }
+ Datum(std::string&& string_) : type(Type::STRING), value(std::move(string_)) { }
+ Datum(const Array& array_) : type(Type::ARRAY), value(array_) { }
+ Datum(Array&& array_) : type(Type::ARRAY), value(std::move(array_)) { }
+ Datum(const Binary& binary) : type(Type::BINARY), value(binary) { }
+ Datum(Binary&& binary) : type(Type::BINARY), value(std::move(binary)) { }
+ Datum(const Time time) : type(Type::TIME), value(time) { }
+ Datum(const Object& object_) : type(Type::OBJECT), value(object_) { }
+ Datum(Object&& object_) : type(Type::OBJECT), value(std::move(object_)) { }
+ Datum(const Datum& other) : type(other.type), value(other.type, other.value) { }
+ Datum(Datum&& other) : type(other.type), value(other.type, std::move(other.value)) { }
+
+ Datum& operator=(const Datum& other) {
+ value.destroy(type);
+ type = other.type;
+ value.set(type, other.value);
+ return *this;
+ }
+
+ Datum& operator=(Datum&& other) {
+ value.destroy(type);
+ type = other.type;
+ value.set(type, std::move(other.value));
+ return *this;
+ }
+
+ Datum(unsigned short number_) : Datum(static_cast<double>(number_)) { }
+ Datum(signed short number_) : Datum(static_cast<double>(number_)) { }
+ Datum(unsigned int number_) : Datum(static_cast<double>(number_)) { }
+ Datum(signed int number_) : Datum(static_cast<double>(number_)) { }
+ Datum(unsigned long number_) : Datum(static_cast<double>(number_)) { }
+ Datum(signed long number_) : Datum(static_cast<double>(number_)) { }
+ Datum(unsigned long long number_) : Datum(static_cast<double>(number_)) { }
+ Datum(signed long long number_) : Datum(static_cast<double>(number_)) { }
+
+ Datum(Protocol::Term::TermType type) : Datum(static_cast<double>(type)) { }
+ Datum(const char* string) : Datum(static_cast<std::string>(string)) { }
+
+ // Cursors are implicitly converted into datums
+ Datum(Cursor&&);
+ Datum(const Cursor&);
+
+ template <class T>
+ Datum(const std::map<std::string, T>& map) : type(Type::OBJECT), value(Object()) {
+ for (const auto& it : map) {
+ value.object.emplace(it.left, Datum(it.right));
+ }
+ }
+
+ template <class T>
+ Datum(std::map<std::string, T>&& map) : type(Type::OBJECT), value(Object()) {
+ for (auto& it : map) {
+ value.object.emplace(it.first, Datum(std::move(it.second)));
+ }
+ }
+
+ template <class T>
+ Datum(const std::vector<T>& vec) : type(Type::ARRAY), value(Array()) {
+ for (const auto& it : vec) {
+ value.array.emplace_back(it);
+ }
+ }
+
+ template <class T>
+ Datum(std::vector<T>&& vec) : type(Type::ARRAY), value(Array()) {
+ for (auto& it : vec) {
+ value.array.emplace_back(std::move(it));
+ }
+ }
+
+ ~Datum() {
+ value.destroy(type);
+ }
+
+ // Apply a visitor
+ template <class R, class F, class ...A>
+ R apply(F f, A&& ...args) const & {
+ switch (type) {
+ case Type::NIL: return f(Nil(), std::forward<A>(args)...); break;
+ case Type::BOOLEAN: return f(value.boolean, std::forward<A>(args)...); break;
+ case Type::NUMBER: return f(value.number, std::forward<A>(args)...); break;
+ case Type::STRING: return f(value.string, std::forward<A>(args)...); break;
+ case Type::OBJECT: return f(value.object, std::forward<A>(args)...); break;
+ case Type::ARRAY: return f(value.array, std::forward<A>(args)...); break;
+ case Type::BINARY: return f(value.binary, std::forward<A>(args)...); break;
+ case Type::TIME: return f(value.time, std::forward<A>(args)...); break;
+ default:
+ throw Error("internal error: no such datum type %d", static_cast<int>(type));
+ }
+ }
+
+ template <class R, class F, class ...A>
+ R apply(F f, A&& ...args) && {
+ switch (type) {
+ case Type::NIL: return f(Nil(), std::forward<A>(args)...); break;
+ case Type::BOOLEAN: return f(std::move(value.boolean), std::forward<A>(args)...); break;
+ case Type::NUMBER: return f(std::move(value.number), std::forward<A>(args)...); break;
+ case Type::STRING: return f(std::move(value.string), std::forward<A>(args)...); break;
+ case Type::OBJECT: return f(std::move(value.object), std::forward<A>(args)...); break;
+ case Type::ARRAY: return f(std::move(value.array), std::forward<A>(args)...); break;
+ case Type::BINARY: return f(std::move(value.binary), std::forward<A>(args)...); break;
+ case Type::TIME: return f(std::move(value.time), std::forward<A>(args)...); break;
+ default:
+ throw Error("internal error: no such datum type %d", static_cast<int>(type));
+ }
+ }
+
+ bool is_nil() const;
+ bool is_boolean() const;
+ bool is_number() const;
+ bool is_string() const;
+ bool is_object() const;
+ bool is_array() const;
+ bool is_binary() const;
+ bool is_time() const;
+
+ // get_* returns nullptr if the datum has a different type
+
+ bool* get_boolean();
+ const bool* get_boolean() const;
+ double* get_number();
+ const double* get_number() const;
+ std::string* get_string();
+ const std::string* get_string() const;
+ Object* get_object();
+ const Object* get_object() const;
+ Datum* get_field(std::string);
+ const Datum* get_field(std::string) const;
+ Array* get_array();
+ const Array* get_array() const;
+ Datum* get_nth(size_t);
+ const Datum* get_nth(size_t) const;
+ Binary* get_binary();
+ const Binary* get_binary() const;
+ Time* get_time();
+ const Time* get_time() const;
+
+ // extract_* throws an exception if the types don't match
+
+ bool& extract_boolean();
+ double& extract_number();
+ std::string& extract_string();
+ Object& extract_object();
+ Datum& extract_field(std::string);
+ Array& extract_array();
+ Datum& extract_nth(size_t);
+ Binary& extract_binary();
+ Time& extract_time();
+
+ // negative, zero or positive if this datum is smaller, identical or larger than the other one, respectively
+ // This is meant to match the results of RethinkDB's comparison operators
+ int compare(const Datum&) const;
+
+ // Deep equality
+ bool operator== (const Datum&) const;
+
+ // Recusively replace non-JSON types into objects that represent them
+ Datum to_raw() const;
+
+ // Recursively replace objects with a $reql_type$ field into the datum they represent
+ Datum from_raw() const;
+
+ template <class json_writer_t> void write_json(json_writer_t *writer) const;
+
+ std::string as_json() const;
+ static Datum from_json(const std::string&);
+
+ bool is_valid() const { return type != Type::INVALID; }
+
+private:
+ enum class Type {
+ INVALID, // default constructed
+ ARRAY, BOOLEAN, NIL, NUMBER, OBJECT, BINARY, STRING, TIME
+ // POINT, LINE, POLYGON
+ };
+ Type type;
+
+ union datum_value {
+ bool boolean;
+ double number;
+ std::string string;
+ Object object;
+ Array array;
+ Binary binary;
+ Time time;
+
+ datum_value() { }
+ datum_value(bool boolean_) : boolean(boolean_) { }
+ datum_value(double number_) : number(number_) { }
+ datum_value(const std::string& string_) : string(string_) { }
+ datum_value(std::string&& string_) : string(std::move(string_)) { }
+ datum_value(const Object& object_) : object(object_) { }
+ datum_value(Object&& object_) : object(std::move(object_)) { }
+ datum_value(const Array& array_) : array(array_) { }
+ datum_value(Array&& array_) : array(std::move(array_)) { }
+ datum_value(const Binary& binary_) : binary(binary_) { }
+ datum_value(Binary&& binary_) : binary(std::move(binary_)) { }
+ datum_value(Time time) : time(std::move(time)) { }
+
+ datum_value(Type type, const datum_value& other){
+ set(type, other);
+ }
+
+ datum_value(Type type, datum_value&& other){
+ set(type, std::move(other));
+ }
+
+ void set(Type type, datum_value&& other) {
+ switch(type){
+ case Type::NIL: case Type::INVALID: break;
+ case Type::BOOLEAN: new (this) bool(other.boolean); break;
+ case Type::NUMBER: new (this) double(other.number); break;
+ case Type::STRING: new (this) std::string(std::move(other.string)); break;
+ case Type::OBJECT: new (this) Object(std::move(other.object)); break;
+ case Type::ARRAY: new (this) Array(std::move(other.array)); break;
+ case Type::BINARY: new (this) Binary(std::move(other.binary)); break;
+ case Type::TIME: new (this) Time(std::move(other.time)); break;
+ }
+ }
+
+ void set(Type type, const datum_value& other) {
+ switch(type){
+ case Type::NIL: case Type::INVALID: break;
+ case Type::BOOLEAN: new (this) bool(other.boolean); break;
+ case Type::NUMBER: new (this) double(other.number); break;
+ case Type::STRING: new (this) std::string(other.string); break;
+ case Type::OBJECT: new (this) Object(other.object); break;
+ case Type::ARRAY: new (this) Array(other.array); break;
+ case Type::BINARY: new (this) Binary(other.binary); break;
+ case Type::TIME: new (this) Time(other.time); break;
+ }
+ }
+
+ void destroy(Type type) {
+ switch(type){
+ case Type::INVALID: break;
+ case Type::NIL: break;
+ case Type::BOOLEAN: break;
+ case Type::NUMBER: break;
+ case Type::STRING: { typedef std::string str; string.~str(); } break;
+ case Type::OBJECT: object.~Object(); break;
+ case Type::ARRAY: array.~Array(); break;
+ case Type::BINARY: binary.~Binary(); break;
+ case Type::TIME: time.~Time(); break;
+ }
+ }
+
+ ~datum_value() { }
+ };
+
+ datum_value value;
+};
+
+}
diff --git a/ext/librethinkdbxx/src/error.h b/ext/librethinkdbxx/src/error.h
new file mode 100644
index 00000000..ab75e248
--- /dev/null
+++ b/ext/librethinkdbxx/src/error.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <cstdarg>
+#include <cstring>
+#include <string>
+#include <cerrno>
+
+namespace RethinkDB {
+
+// All errors thrown by the server have this type
+struct Error {
+ template <class ...T>
+ explicit Error(const char* format_, T... A) {
+ format(format_, A...);
+ }
+
+ Error() = default;
+ Error(Error&&) = default;
+ Error(const Error&) = default;
+
+ Error& operator= (Error&& other) {
+ message = std::move(other.message);
+ return *this;
+ }
+
+ static Error from_errno(const char* str){
+ return Error("%s: %s", str, strerror(errno));
+ }
+
+ // The error message
+ std::string message;
+
+private:
+ const size_t max_message_size = 2048;
+
+ void format(const char* format_, ...) {
+ va_list args;
+ va_start(args, format_);
+ char message_[max_message_size];
+ vsnprintf(message_, max_message_size, format_, args);
+ va_end(args);
+ message = message_;
+ }
+};
+
+}
diff --git a/ext/librethinkdbxx/src/exceptions.h b/ext/librethinkdbxx/src/exceptions.h
new file mode 100644
index 00000000..08c0b0a0
--- /dev/null
+++ b/ext/librethinkdbxx/src/exceptions.h
@@ -0,0 +1,13 @@
+#ifndef EXCEPTIONS_H
+#define EXCEPTIONS_H
+
+namespace RethinkDB {
+
+class TimeoutException : public std::exception {
+public:
+ const char *what() const throw () { return "operation timed out"; }
+};
+
+}
+
+#endif // EXCEPTIONS_H
diff --git a/ext/librethinkdbxx/src/json.cc b/ext/librethinkdbxx/src/json.cc
new file mode 100644
index 00000000..c908eefb
--- /dev/null
+++ b/ext/librethinkdbxx/src/json.cc
@@ -0,0 +1,62 @@
+#include "json_p.h"
+#include "error.h"
+#include "utils.h"
+
+#include "rapidjson-config.h"
+#include "rapidjson/document.h"
+#include "rapidjson/stringbuffer.h"
+#include "rapidjson/writer.h"
+#include "rapidjson/prettywriter.h"
+
+namespace RethinkDB {
+
+Datum read_datum(const std::string& json) {
+ rapidjson::Document document;
+ document.Parse(json);
+ return read_datum(document);
+}
+
+Datum read_datum(const rapidjson::Value &json) {
+ switch(json.GetType()) {
+ case rapidjson::kNullType: return Nil();
+ case rapidjson::kFalseType: return false;
+ case rapidjson::kTrueType: return true;
+ case rapidjson::kNumberType: return json.GetDouble();
+ case rapidjson::kStringType:
+ return std::string(json.GetString(), json.GetStringLength());
+
+ case rapidjson::kObjectType: {
+ Object result;
+ for (rapidjson::Value::ConstMemberIterator it = json.MemberBegin();
+ it != json.MemberEnd(); ++it) {
+ result.insert(std::make_pair(std::string(it->name.GetString(),
+ it->name.GetStringLength()),
+ read_datum(it->value)));
+ }
+
+ if (result.count("$reql_type$"))
+ return Datum(std::move(result)).from_raw();
+ return std::move(result);
+ } break;
+ case rapidjson::kArrayType: {
+ Array result;
+ result.reserve(json.Size());
+ for (rapidjson::Value::ConstValueIterator it = json.Begin();
+ it != json.End(); ++it) {
+ result.push_back(read_datum(*it));
+ }
+ return std::move(result);
+ } break;
+ default:
+ throw Error("invalid rapidjson value");
+ }
+}
+
+std::string write_datum(const Datum& datum) {
+ rapidjson::StringBuffer buffer;
+ rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
+ datum.write_json(&writer);
+ return std::string(buffer.GetString(), buffer.GetSize());
+}
+
+}
diff --git a/ext/librethinkdbxx/src/json_p.h b/ext/librethinkdbxx/src/json_p.h
new file mode 100644
index 00000000..ebf537a9
--- /dev/null
+++ b/ext/librethinkdbxx/src/json_p.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "datum.h"
+
+namespace rapidjson {
+ class CrtAllocator;
+ template<typename> struct UTF8;
+ template <typename, typename> class GenericValue;
+ template <typename> class MemoryPoolAllocator;
+ typedef GenericValue<UTF8<char>, MemoryPoolAllocator<CrtAllocator> > Value;
+}
+
+namespace RethinkDB {
+
+Datum read_datum(const std::string&);
+Datum read_datum(const rapidjson::Value &json);
+std::string write_datum(const Datum&);
+
+}
diff --git a/ext/librethinkdbxx/src/rapidjson-config.h b/ext/librethinkdbxx/src/rapidjson-config.h
new file mode 100644
index 00000000..320c4048
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson-config.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#define RAPIDJSON_HAS_STDSTRING 1
+#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
+#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1
+#define RAPIDJSON_HAS_CXX11_TYPETRAITS 1
+#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1
+#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseFullPrecisionFlag
diff --git a/ext/librethinkdbxx/src/rapidjson/allocators.h b/ext/librethinkdbxx/src/rapidjson/allocators.h
new file mode 100644
index 00000000..c7059697
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/allocators.h
@@ -0,0 +1,263 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_ALLOCATORS_H_
+#define RAPIDJSON_ALLOCATORS_H_
+
+#include "rapidjson.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// Allocator
+
+/*! \class rapidjson::Allocator
+ \brief Concept for allocating, resizing and freeing memory block.
+
+ Note that Malloc() and Realloc() are non-static but Free() is static.
+
+ So if an allocator need to support Free(), it needs to put its pointer in
+ the header of memory block.
+
+\code
+concept Allocator {
+ static const bool kNeedFree; //!< Whether this allocator needs to call Free().
+
+ // Allocate a memory block.
+ // \param size of the memory block in bytes.
+ // \returns pointer to the memory block.
+ void* Malloc(size_t size);
+
+ // Resize a memory block.
+ // \param originalPtr The pointer to current memory block. Null pointer is permitted.
+ // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.)
+ // \param newSize the new size in bytes.
+ void* Realloc(void* originalPtr, size_t originalSize, size_t newSize);
+
+ // Free a memory block.
+ // \param pointer to the memory block. Null pointer is permitted.
+ static void Free(void *ptr);
+};
+\endcode
+*/
+
+///////////////////////////////////////////////////////////////////////////////
+// CrtAllocator
+
+//! C-runtime library allocator.
+/*! This class is just wrapper for standard C library memory routines.
+ \note implements Allocator concept
+*/
+class CrtAllocator {
+public:
+ static const bool kNeedFree = true;
+ void* Malloc(size_t size) {
+ if (size) // behavior of malloc(0) is implementation defined.
+ return std::malloc(size);
+ else
+ return NULL; // standardize to returning NULL.
+ }
+ void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
+ (void)originalSize;
+ if (newSize == 0) {
+ std::free(originalPtr);
+ return NULL;
+ }
+ return std::realloc(originalPtr, newSize);
+ }
+ static void Free(void *ptr) { std::free(ptr); }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// MemoryPoolAllocator
+
+//! Default memory allocator used by the parser and DOM.
+/*! This allocator allocate memory blocks from pre-allocated memory chunks.
+
+ It does not free memory blocks. And Realloc() only allocate new memory.
+
+ The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default.
+
+ User may also supply a buffer as the first chunk.
+
+ If the user-buffer is full then additional chunks are allocated by BaseAllocator.
+
+ The user-buffer is not deallocated by this allocator.
+
+ \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator.
+ \note implements Allocator concept
+*/
+template <typename BaseAllocator = CrtAllocator>
+class MemoryPoolAllocator {
+public:
+ static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator)
+
+ //! Constructor with chunkSize.
+ /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
+ \param baseAllocator The allocator for allocating memory chunks.
+ */
+ MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
+ chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
+ {
+ }
+
+ //! Constructor with user-supplied buffer.
+ /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size.
+
+ The user buffer will not be deallocated when this allocator is destructed.
+
+ \param buffer User supplied buffer.
+ \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader).
+ \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
+ \param baseAllocator The allocator for allocating memory chunks.
+ */
+ MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
+ chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
+ {
+ RAPIDJSON_ASSERT(buffer != 0);
+ RAPIDJSON_ASSERT(size > sizeof(ChunkHeader));
+ chunkHead_ = reinterpret_cast<ChunkHeader*>(buffer);
+ chunkHead_->capacity = size - sizeof(ChunkHeader);
+ chunkHead_->size = 0;
+ chunkHead_->next = 0;
+ }
+
+ //! Destructor.
+ /*! This deallocates all memory chunks, excluding the user-supplied buffer.
+ */
+ ~MemoryPoolAllocator() {
+ Clear();
+ RAPIDJSON_DELETE(ownBaseAllocator_);
+ }
+
+ //! Deallocates all memory chunks, excluding the user-supplied buffer.
+ void Clear() {
+ while (chunkHead_ && chunkHead_ != userBuffer_) {
+ ChunkHeader* next = chunkHead_->next;
+ baseAllocator_->Free(chunkHead_);
+ chunkHead_ = next;
+ }
+ if (chunkHead_ && chunkHead_ == userBuffer_)
+ chunkHead_->size = 0; // Clear user buffer
+ }
+
+ //! Computes the total capacity of allocated memory chunks.
+ /*! \return total capacity in bytes.
+ */
+ size_t Capacity() const {
+ size_t capacity = 0;
+ for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
+ capacity += c->capacity;
+ return capacity;
+ }
+
+ //! Computes the memory blocks allocated.
+ /*! \return total used bytes.
+ */
+ size_t Size() const {
+ size_t size = 0;
+ for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
+ size += c->size;
+ return size;
+ }
+
+ //! Allocates a memory block. (concept Allocator)
+ void* Malloc(size_t size) {
+ if (!size)
+ return NULL;
+
+ size = RAPIDJSON_ALIGN(size);
+ if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity)
+ AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size);
+
+ void *buffer = reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size;
+ chunkHead_->size += size;
+ return buffer;
+ }
+
+ //! Resizes a memory block (concept Allocator)
+ void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
+ if (originalPtr == 0)
+ return Malloc(newSize);
+
+ if (newSize == 0)
+ return NULL;
+
+ originalSize = RAPIDJSON_ALIGN(originalSize);
+ newSize = RAPIDJSON_ALIGN(newSize);
+
+ // Do not shrink if new size is smaller than original
+ if (originalSize >= newSize)
+ return originalPtr;
+
+ // Simply expand it if it is the last allocation and there is sufficient space
+ if (originalPtr == reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) {
+ size_t increment = static_cast<size_t>(newSize - originalSize);
+ if (chunkHead_->size + increment <= chunkHead_->capacity) {
+ chunkHead_->size += increment;
+ return originalPtr;
+ }
+ }
+
+ // Realloc process: allocate and copy memory, do not free original buffer.
+ void* newBuffer = Malloc(newSize);
+ RAPIDJSON_ASSERT(newBuffer != 0); // Do not handle out-of-memory explicitly.
+ if (originalSize)
+ std::memcpy(newBuffer, originalPtr, originalSize);
+ return newBuffer;
+ }
+
+ //! Frees a memory block (concept Allocator)
+ static void Free(void *ptr) { (void)ptr; } // Do nothing
+
+private:
+ //! Copy constructor is not permitted.
+ MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */;
+ //! Copy assignment operator is not permitted.
+ MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */;
+
+ //! Creates a new chunk.
+ /*! \param capacity Capacity of the chunk in bytes.
+ */
+ void AddChunk(size_t capacity) {
+ if (!baseAllocator_)
+ ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator());
+ ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity));
+ chunk->capacity = capacity;
+ chunk->size = 0;
+ chunk->next = chunkHead_;
+ chunkHead_ = chunk;
+ }
+
+ static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity.
+
+ //! Chunk header for perpending to each chunk.
+ /*! Chunks are stored as a singly linked list.
+ */
+ struct ChunkHeader {
+ size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself).
+ size_t size; //!< Current size of allocated memory in bytes.
+ ChunkHeader *next; //!< Next chunk in the linked list.
+ };
+
+ ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation.
+ size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated.
+ void *userBuffer_; //!< User supplied buffer.
+ BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks.
+ BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object.
+};
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_ENCODINGS_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/document.h b/ext/librethinkdbxx/src/rapidjson/document.h
new file mode 100644
index 00000000..f7f846f2
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/document.h
@@ -0,0 +1,2575 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_DOCUMENT_H_
+#define RAPIDJSON_DOCUMENT_H_
+
+/*! \file document.h */
+
+#include "reader.h"
+#include "internal/meta.h"
+#include "internal/strfunc.h"
+#include "memorystream.h"
+#include "encodedstream.h"
+#include <new> // placement new
+#include <limits>
+
+RAPIDJSON_DIAG_PUSH
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
+RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_OFF(padded)
+RAPIDJSON_DIAG_OFF(switch-enum)
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#endif
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_OFF(effc++)
+#if __GNUC__ >= 6
+RAPIDJSON_DIAG_OFF(terminate) // ignore throwing RAPIDJSON_ASSERT in RAPIDJSON_NOEXCEPT functions
+#endif
+#endif // __GNUC__
+
+#ifndef RAPIDJSON_NOMEMBERITERATORCLASS
+#include <iterator> // std::iterator, std::random_access_iterator_tag
+#endif
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+#include <utility> // std::move
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+// Forward declaration.
+template <typename Encoding, typename Allocator>
+class GenericValue;
+
+template <typename Encoding, typename Allocator, typename StackAllocator>
+class GenericDocument;
+
+//! Name-value pair in a JSON object value.
+/*!
+ This class was internal to GenericValue. It used to be a inner struct.
+ But a compiler (IBM XL C/C++ for AIX) have reported to have problem with that so it moved as a namespace scope struct.
+ https://code.google.com/p/rapidjson/issues/detail?id=64
+*/
+template <typename Encoding, typename Allocator>
+struct GenericMember {
+ GenericValue<Encoding, Allocator> name; //!< name of member (must be a string)
+ GenericValue<Encoding, Allocator> value; //!< value of member.
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericMemberIterator
+
+#ifndef RAPIDJSON_NOMEMBERITERATORCLASS
+
+//! (Constant) member iterator for a JSON object value
+/*!
+ \tparam Const Is this a constant iterator?
+ \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document)
+ \tparam Allocator Allocator type for allocating memory of object, array and string.
+
+ This class implements a Random Access Iterator for GenericMember elements
+ of a GenericValue, see ISO/IEC 14882:2003(E) C++ standard, 24.1 [lib.iterator.requirements].
+
+ \note This iterator implementation is mainly intended to avoid implicit
+ conversions from iterator values to \c NULL,
+ e.g. from GenericValue::FindMember.
+
+ \note Define \c RAPIDJSON_NOMEMBERITERATORCLASS to fall back to a
+ pointer-based implementation, if your platform doesn't provide
+ the C++ <iterator> header.
+
+ \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator
+ */
+template <bool Const, typename Encoding, typename Allocator>
+class GenericMemberIterator
+ : public std::iterator<std::random_access_iterator_tag
+ , typename internal::MaybeAddConst<Const,GenericMember<Encoding,Allocator> >::Type> {
+
+ friend class GenericValue<Encoding,Allocator>;
+ template <bool, typename, typename> friend class GenericMemberIterator;
+
+ typedef GenericMember<Encoding,Allocator> PlainType;
+ typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType;
+ typedef std::iterator<std::random_access_iterator_tag,ValueType> BaseType;
+
+public:
+ //! Iterator type itself
+ typedef GenericMemberIterator Iterator;
+ //! Constant iterator type
+ typedef GenericMemberIterator<true,Encoding,Allocator> ConstIterator;
+ //! Non-constant iterator type
+ typedef GenericMemberIterator<false,Encoding,Allocator> NonConstIterator;
+
+ //! Pointer to (const) GenericMember
+ typedef typename BaseType::pointer Pointer;
+ //! Reference to (const) GenericMember
+ typedef typename BaseType::reference Reference;
+ //! Signed integer type (e.g. \c ptrdiff_t)
+ typedef typename BaseType::difference_type DifferenceType;
+
+ //! Default constructor (singular value)
+ /*! Creates an iterator pointing to no element.
+ \note All operations, except for comparisons, are undefined on such values.
+ */
+ GenericMemberIterator() : ptr_() {}
+
+ //! Iterator conversions to more const
+ /*!
+ \param it (Non-const) iterator to copy from
+
+ Allows the creation of an iterator from another GenericMemberIterator
+ that is "less const". Especially, creating a non-constant iterator
+ from a constant iterator are disabled:
+ \li const -> non-const (not ok)
+ \li const -> const (ok)
+ \li non-const -> const (ok)
+ \li non-const -> non-const (ok)
+
+ \note If the \c Const template parameter is already \c false, this
+ constructor effectively defines a regular copy-constructor.
+ Otherwise, the copy constructor is implicitly defined.
+ */
+ GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {}
+ Iterator& operator=(const NonConstIterator & it) { ptr_ = it.ptr_; return *this; }
+
+ //! @name stepping
+ //@{
+ Iterator& operator++(){ ++ptr_; return *this; }
+ Iterator& operator--(){ --ptr_; return *this; }
+ Iterator operator++(int){ Iterator old(*this); ++ptr_; return old; }
+ Iterator operator--(int){ Iterator old(*this); --ptr_; return old; }
+ //@}
+
+ //! @name increment/decrement
+ //@{
+ Iterator operator+(DifferenceType n) const { return Iterator(ptr_+n); }
+ Iterator operator-(DifferenceType n) const { return Iterator(ptr_-n); }
+
+ Iterator& operator+=(DifferenceType n) { ptr_+=n; return *this; }
+ Iterator& operator-=(DifferenceType n) { ptr_-=n; return *this; }
+ //@}
+
+ //! @name relations
+ //@{
+ bool operator==(ConstIterator that) const { return ptr_ == that.ptr_; }
+ bool operator!=(ConstIterator that) const { return ptr_ != that.ptr_; }
+ bool operator<=(ConstIterator that) const { return ptr_ <= that.ptr_; }
+ bool operator>=(ConstIterator that) const { return ptr_ >= that.ptr_; }
+ bool operator< (ConstIterator that) const { return ptr_ < that.ptr_; }
+ bool operator> (ConstIterator that) const { return ptr_ > that.ptr_; }
+ //@}
+
+ //! @name dereference
+ //@{
+ Reference operator*() const { return *ptr_; }
+ Pointer operator->() const { return ptr_; }
+ Reference operator[](DifferenceType n) const { return ptr_[n]; }
+ //@}
+
+ //! Distance
+ DifferenceType operator-(ConstIterator that) const { return ptr_-that.ptr_; }
+
+private:
+ //! Internal constructor from plain pointer
+ explicit GenericMemberIterator(Pointer p) : ptr_(p) {}
+
+ Pointer ptr_; //!< raw pointer
+};
+
+#else // RAPIDJSON_NOMEMBERITERATORCLASS
+
+// class-based member iterator implementation disabled, use plain pointers
+
+template <bool Const, typename Encoding, typename Allocator>
+struct GenericMemberIterator;
+
+//! non-const GenericMemberIterator
+template <typename Encoding, typename Allocator>
+struct GenericMemberIterator<false,Encoding,Allocator> {
+ //! use plain pointer as iterator type
+ typedef GenericMember<Encoding,Allocator>* Iterator;
+};
+//! const GenericMemberIterator
+template <typename Encoding, typename Allocator>
+struct GenericMemberIterator<true,Encoding,Allocator> {
+ //! use plain const pointer as iterator type
+ typedef const GenericMember<Encoding,Allocator>* Iterator;
+};
+
+#endif // RAPIDJSON_NOMEMBERITERATORCLASS
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericStringRef
+
+//! Reference to a constant string (not taking a copy)
+/*!
+ \tparam CharType character type of the string
+
+ This helper class is used to automatically infer constant string
+ references for string literals, especially from \c const \b (!)
+ character arrays.
+
+ The main use is for creating JSON string values without copying the
+ source string via an \ref Allocator. This requires that the referenced
+ string pointers have a sufficient lifetime, which exceeds the lifetime
+ of the associated GenericValue.
+
+ \b Example
+ \code
+ Value v("foo"); // ok, no need to copy & calculate length
+ const char foo[] = "foo";
+ v.SetString(foo); // ok
+
+ const char* bar = foo;
+ // Value x(bar); // not ok, can't rely on bar's lifetime
+ Value x(StringRef(bar)); // lifetime explicitly guaranteed by user
+ Value y(StringRef(bar, 3)); // ok, explicitly pass length
+ \endcode
+
+ \see StringRef, GenericValue::SetString
+*/
+template<typename CharType>
+struct GenericStringRef {
+ typedef CharType Ch; //!< character type of the string
+
+ //! Create string reference from \c const character array
+#ifndef __clang__ // -Wdocumentation
+ /*!
+ This constructor implicitly creates a constant string reference from
+ a \c const character array. It has better performance than
+ \ref StringRef(const CharType*) by inferring the string \ref length
+ from the array length, and also supports strings containing null
+ characters.
+
+ \tparam N length of the string, automatically inferred
+
+ \param str Constant character array, lifetime assumed to be longer
+ than the use of the string in e.g. a GenericValue
+
+ \post \ref s == str
+
+ \note Constant complexity.
+ \note There is a hidden, private overload to disallow references to
+ non-const character arrays to be created via this constructor.
+ By this, e.g. function-scope arrays used to be filled via
+ \c snprintf are excluded from consideration.
+ In such cases, the referenced string should be \b copied to the
+ GenericValue instead.
+ */
+#endif
+ template<SizeType N>
+ GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT
+ : s(str), length(N-1) {}
+
+ //! Explicitly create string reference from \c const character pointer
+#ifndef __clang__ // -Wdocumentation
+ /*!
+ This constructor can be used to \b explicitly create a reference to
+ a constant string pointer.
+
+ \see StringRef(const CharType*)
+
+ \param str Constant character pointer, lifetime assumed to be longer
+ than the use of the string in e.g. a GenericValue
+
+ \post \ref s == str
+
+ \note There is a hidden, private overload to disallow references to
+ non-const character arrays to be created via this constructor.
+ By this, e.g. function-scope arrays used to be filled via
+ \c snprintf are excluded from consideration.
+ In such cases, the referenced string should be \b copied to the
+ GenericValue instead.
+ */
+#endif
+ explicit GenericStringRef(const CharType* str)
+ : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != 0); }
+
+ //! Create constant string reference from pointer and length
+#ifndef __clang__ // -Wdocumentation
+ /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue
+ \param len length of the string, excluding the trailing NULL terminator
+
+ \post \ref s == str && \ref length == len
+ \note Constant complexity.
+ */
+#endif
+ GenericStringRef(const CharType* str, SizeType len)
+ : s(str), length(len) { RAPIDJSON_ASSERT(s != 0); }
+
+ GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {}
+
+ GenericStringRef& operator=(const GenericStringRef& rhs) { s = rhs.s; length = rhs.length; }
+
+ //! implicit conversion to plain CharType pointer
+ operator const Ch *() const { return s; }
+
+ const Ch* const s; //!< plain CharType pointer
+ const SizeType length; //!< length of the string (excluding the trailing NULL terminator)
+
+private:
+ //! Disallow construction from non-const array
+ template<SizeType N>
+ GenericStringRef(CharType (&str)[N]) /* = delete */;
+};
+
+//! Mark a character pointer as constant string
+/*! Mark a plain character pointer as a "string literal". This function
+ can be used to avoid copying a character string to be referenced as a
+ value in a JSON GenericValue object, if the string's lifetime is known
+ to be valid long enough.
+ \tparam CharType Character type of the string
+ \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue
+ \return GenericStringRef string reference object
+ \relatesalso GenericStringRef
+
+ \see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember
+*/
+template<typename CharType>
+inline GenericStringRef<CharType> StringRef(const CharType* str) {
+ return GenericStringRef<CharType>(str, internal::StrLen(str));
+}
+
+//! Mark a character pointer as constant string
+/*! Mark a plain character pointer as a "string literal". This function
+ can be used to avoid copying a character string to be referenced as a
+ value in a JSON GenericValue object, if the string's lifetime is known
+ to be valid long enough.
+
+ This version has better performance with supplied length, and also
+ supports string containing null characters.
+
+ \tparam CharType character type of the string
+ \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue
+ \param length The length of source string.
+ \return GenericStringRef string reference object
+ \relatesalso GenericStringRef
+*/
+template<typename CharType>
+inline GenericStringRef<CharType> StringRef(const CharType* str, size_t length) {
+ return GenericStringRef<CharType>(str, SizeType(length));
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+//! Mark a string object as constant string
+/*! Mark a string object (e.g. \c std::string) as a "string literal".
+ This function can be used to avoid copying a string to be referenced as a
+ value in a JSON GenericValue object, if the string's lifetime is known
+ to be valid long enough.
+
+ \tparam CharType character type of the string
+ \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue
+ \return GenericStringRef string reference object
+ \relatesalso GenericStringRef
+ \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING.
+*/
+template<typename CharType>
+inline GenericStringRef<CharType> StringRef(const std::basic_string<CharType>& str) {
+ return GenericStringRef<CharType>(str.data(), SizeType(str.size()));
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericValue type traits
+namespace internal {
+
+template <typename T, typename Encoding = void, typename Allocator = void>
+struct IsGenericValueImpl : FalseType {};
+
+// select candidates according to nested encoding and allocator types
+template <typename T> struct IsGenericValueImpl<T, typename Void<typename T::EncodingType>::Type, typename Void<typename T::AllocatorType>::Type>
+ : IsBaseOf<GenericValue<typename T::EncodingType, typename T::AllocatorType>, T>::Type {};
+
+// helper to match arbitrary GenericValue instantiations, including derived classes
+template <typename T> struct IsGenericValue : IsGenericValueImpl<T>::Type {};
+
+} // namespace internal
+
+///////////////////////////////////////////////////////////////////////////////
+// TypeHelper
+
+namespace internal {
+
+template <typename ValueType, typename T>
+struct TypeHelper {};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, bool> {
+ static bool Is(const ValueType& v) { return v.IsBool(); }
+ static bool Get(const ValueType& v) { return v.GetBool(); }
+ static ValueType& Set(ValueType& v, bool data) { return v.SetBool(data); }
+ static ValueType& Set(ValueType& v, bool data, typename ValueType::AllocatorType&) { return v.SetBool(data); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, int> {
+ static bool Is(const ValueType& v) { return v.IsInt(); }
+ static int Get(const ValueType& v) { return v.GetInt(); }
+ static ValueType& Set(ValueType& v, int data) { return v.SetInt(data); }
+ static ValueType& Set(ValueType& v, int data, typename ValueType::AllocatorType&) { return v.SetInt(data); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, unsigned> {
+ static bool Is(const ValueType& v) { return v.IsUint(); }
+ static unsigned Get(const ValueType& v) { return v.GetUint(); }
+ static ValueType& Set(ValueType& v, unsigned data) { return v.SetUint(data); }
+ static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, int64_t> {
+ static bool Is(const ValueType& v) { return v.IsInt64(); }
+ static int64_t Get(const ValueType& v) { return v.GetInt64(); }
+ static ValueType& Set(ValueType& v, int64_t data) { return v.SetInt64(data); }
+ static ValueType& Set(ValueType& v, int64_t data, typename ValueType::AllocatorType&) { return v.SetInt64(data); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, uint64_t> {
+ static bool Is(const ValueType& v) { return v.IsUint64(); }
+ static uint64_t Get(const ValueType& v) { return v.GetUint64(); }
+ static ValueType& Set(ValueType& v, uint64_t data) { return v.SetUint64(data); }
+ static ValueType& Set(ValueType& v, uint64_t data, typename ValueType::AllocatorType&) { return v.SetUint64(data); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, double> {
+ static bool Is(const ValueType& v) { return v.IsDouble(); }
+ static double Get(const ValueType& v) { return v.GetDouble(); }
+ static ValueType& Set(ValueType& v, double data) { return v.SetDouble(data); }
+ static ValueType& Set(ValueType& v, double data, typename ValueType::AllocatorType&) { return v.SetDouble(data); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, float> {
+ static bool Is(const ValueType& v) { return v.IsFloat(); }
+ static float Get(const ValueType& v) { return v.GetFloat(); }
+ static ValueType& Set(ValueType& v, float data) { return v.SetFloat(data); }
+ static ValueType& Set(ValueType& v, float data, typename ValueType::AllocatorType&) { return v.SetFloat(data); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, const typename ValueType::Ch*> {
+ typedef const typename ValueType::Ch* StringType;
+ static bool Is(const ValueType& v) { return v.IsString(); }
+ static StringType Get(const ValueType& v) { return v.GetString(); }
+ static ValueType& Set(ValueType& v, const StringType data) { return v.SetString(typename ValueType::StringRefType(data)); }
+ static ValueType& Set(ValueType& v, const StringType data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); }
+};
+
+#if RAPIDJSON_HAS_STDSTRING
+template<typename ValueType>
+struct TypeHelper<ValueType, std::basic_string<typename ValueType::Ch> > {
+ typedef std::basic_string<typename ValueType::Ch> StringType;
+ static bool Is(const ValueType& v) { return v.IsString(); }
+ static StringType Get(const ValueType& v) { return v.GetString(); }
+ static ValueType& Set(ValueType& v, const StringType& data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); }
+};
+#endif
+
+template<typename ValueType>
+struct TypeHelper<ValueType, typename ValueType::Array> {
+ typedef typename ValueType::Array ArrayType;
+ static bool Is(const ValueType& v) { return v.IsArray(); }
+ static ArrayType Get(ValueType& v) { return v.GetArray(); }
+ static ValueType& Set(ValueType& v, ArrayType data) { return v = data; }
+ static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v = data; }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, typename ValueType::ConstArray> {
+ typedef typename ValueType::ConstArray ArrayType;
+ static bool Is(const ValueType& v) { return v.IsArray(); }
+ static ArrayType Get(const ValueType& v) { return v.GetArray(); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, typename ValueType::Object> {
+ typedef typename ValueType::Object ObjectType;
+ static bool Is(const ValueType& v) { return v.IsObject(); }
+ static ObjectType Get(ValueType& v) { return v.GetObject(); }
+ static ValueType& Set(ValueType& v, ObjectType data) { return v = data; }
+ static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { v = data; }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, typename ValueType::ConstObject> {
+ typedef typename ValueType::ConstObject ObjectType;
+ static bool Is(const ValueType& v) { return v.IsObject(); }
+ static ObjectType Get(const ValueType& v) { return v.GetObject(); }
+};
+
+} // namespace internal
+
+// Forward declarations
+template <bool, typename> class GenericArray;
+template <bool, typename> class GenericObject;
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericValue
+
+//! Represents a JSON value. Use Value for UTF8 encoding and default allocator.
+/*!
+ A JSON value can be one of 7 types. This class is a variant type supporting
+ these types.
+
+ Use the Value if UTF8 and default allocator
+
+ \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document)
+ \tparam Allocator Allocator type for allocating memory of object, array and string.
+*/
+template <typename Encoding, typename Allocator = MemoryPoolAllocator<> >
+class GenericValue {
+public:
+ //! Name-value pair in an object.
+ typedef GenericMember<Encoding, Allocator> Member;
+ typedef Encoding EncodingType; //!< Encoding type from template parameter.
+ typedef Allocator AllocatorType; //!< Allocator type from template parameter.
+ typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding.
+ typedef GenericStringRef<Ch> StringRefType; //!< Reference to a constant string
+ typedef typename GenericMemberIterator<false,Encoding,Allocator>::Iterator MemberIterator; //!< Member iterator for iterating in object.
+ typedef typename GenericMemberIterator<true,Encoding,Allocator>::Iterator ConstMemberIterator; //!< Constant member iterator for iterating in object.
+ typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array.
+ typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array.
+ typedef GenericValue<Encoding, Allocator> ValueType; //!< Value type of itself.
+ typedef GenericArray<false, ValueType> Array;
+ typedef GenericArray<true, ValueType> ConstArray;
+ typedef GenericObject<false, ValueType> Object;
+ typedef GenericObject<true, ValueType> ConstObject;
+
+ //!@name Constructors and destructor.
+ //@{
+
+ //! Default constructor creates a null value.
+ GenericValue() RAPIDJSON_NOEXCEPT : data_() { data_.f.flags = kNullFlag; }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ //! Move constructor in C++11
+ GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_) {
+ rhs.data_.f.flags = kNullFlag; // give up contents
+ }
+#endif
+
+private:
+ //! Copy constructor is not permitted.
+ GenericValue(const GenericValue& rhs);
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ //! Moving from a GenericDocument is not permitted.
+ template <typename StackAllocator>
+ GenericValue(GenericDocument<Encoding,Allocator,StackAllocator>&& rhs);
+
+ //! Move assignment from a GenericDocument is not permitted.
+ template <typename StackAllocator>
+ GenericValue& operator=(GenericDocument<Encoding,Allocator,StackAllocator>&& rhs);
+#endif
+
+public:
+
+ //! Constructor with JSON value type.
+ /*! This creates a Value of specified type with default content.
+ \param type Type of the value.
+ \note Default content for number is zero.
+ */
+ explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() {
+ static const uint16_t defaultFlags[7] = {
+ kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag,
+ kNumberAnyFlag
+ };
+ RAPIDJSON_ASSERT(type <= kNumberType);
+ data_.f.flags = defaultFlags[type];
+
+ // Use ShortString to store empty string.
+ if (type == kStringType)
+ data_.ss.SetLength(0);
+ }
+
+ //! Explicit copy constructor (with allocator)
+ /*! Creates a copy of a Value by using the given Allocator
+ \tparam SourceAllocator allocator of \c rhs
+ \param rhs Value to copy from (read-only)
+ \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator().
+ \see CopyFrom()
+ */
+ template< typename SourceAllocator >
+ GenericValue(const GenericValue<Encoding, SourceAllocator>& rhs, Allocator & allocator);
+
+ //! Constructor for boolean value.
+ /*! \param b Boolean value
+ \note This constructor is limited to \em real boolean values and rejects
+ implicitly converted types like arbitrary pointers. Use an explicit cast
+ to \c bool, if you want to construct a boolean JSON value in such cases.
+ */
+#ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen
+ template <typename T>
+ explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame<bool, T>))) RAPIDJSON_NOEXCEPT // See #472
+#else
+ explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT
+#endif
+ : data_() {
+ // safe-guard against failing SFINAE
+ RAPIDJSON_STATIC_ASSERT((internal::IsSame<bool,T>::Value));
+ data_.f.flags = b ? kTrueFlag : kFalseFlag;
+ }
+
+ //! Constructor for int value.
+ explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() {
+ data_.n.i64 = i;
+ data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag;
+ }
+
+ //! Constructor for unsigned value.
+ explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() {
+ data_.n.u64 = u;
+ data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag);
+ }
+
+ //! Constructor for int64_t value.
+ explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_() {
+ data_.n.i64 = i64;
+ data_.f.flags = kNumberInt64Flag;
+ if (i64 >= 0) {
+ data_.f.flags |= kNumberUint64Flag;
+ if (!(static_cast<uint64_t>(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000)))
+ data_.f.flags |= kUintFlag;
+ if (!(static_cast<uint64_t>(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000)))
+ data_.f.flags |= kIntFlag;
+ }
+ else if (i64 >= static_cast<int64_t>(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000)))
+ data_.f.flags |= kIntFlag;
+ }
+
+ //! Constructor for uint64_t value.
+ explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_() {
+ data_.n.u64 = u64;
+ data_.f.flags = kNumberUint64Flag;
+ if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000)))
+ data_.f.flags |= kInt64Flag;
+ if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000)))
+ data_.f.flags |= kUintFlag;
+ if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000)))
+ data_.f.flags |= kIntFlag;
+ }
+
+ //! Constructor for double value.
+ explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; }
+
+ //! Constructor for constant string (i.e. do not make a copy of string)
+ GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); }
+
+ //! Constructor for constant string (i.e. do not make a copy of string)
+ explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); }
+
+ //! Constructor for copy-string (i.e. do make a copy of string)
+ GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_() { SetStringRaw(StringRef(s, length), allocator); }
+
+ //! Constructor for copy-string (i.e. do make a copy of string)
+ GenericValue(const Ch*s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Constructor for copy-string from a string object (i.e. do make a copy of string)
+ /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING.
+ */
+ GenericValue(const std::basic_string<Ch>& s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); }
+#endif
+
+ //! Constructor for Array.
+ /*!
+ \param a An array obtained by \c GetArray().
+ \note \c Array is always pass-by-value.
+ \note the source array is moved into this value and the sourec array becomes empty.
+ */
+ GenericValue(Array a) RAPIDJSON_NOEXCEPT : data_(a.value_.data_) {
+ a.value_.data_ = Data();
+ a.value_.data_.f.flags = kArrayFlag;
+ }
+
+ //! Constructor for Object.
+ /*!
+ \param o An object obtained by \c GetObject().
+ \note \c Object is always pass-by-value.
+ \note the source object is moved into this value and the sourec object becomes empty.
+ */
+ GenericValue(Object o) RAPIDJSON_NOEXCEPT : data_(o.value_.data_) {
+ o.value_.data_ = Data();
+ o.value_.data_.f.flags = kObjectFlag;
+ }
+
+ //! Destructor.
+ /*! Need to destruct elements of array, members of object, or copy-string.
+ */
+ ~GenericValue() {
+ if (Allocator::kNeedFree) { // Shortcut by Allocator's trait
+ switch(data_.f.flags) {
+ case kArrayFlag:
+ {
+ GenericValue* e = GetElementsPointer();
+ for (GenericValue* v = e; v != e + data_.a.size; ++v)
+ v->~GenericValue();
+ Allocator::Free(e);
+ }
+ break;
+
+ case kObjectFlag:
+ for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m)
+ m->~Member();
+ Allocator::Free(GetMembersPointer());
+ break;
+
+ case kCopyStringFlag:
+ Allocator::Free(const_cast<Ch*>(GetStringPointer()));
+ break;
+
+ default:
+ break; // Do nothing for other types.
+ }
+ }
+ }
+
+ //@}
+
+ //!@name Assignment operators
+ //@{
+
+ //! Assignment with move semantics.
+ /*! \param rhs Source of the assignment. It will become a null value after assignment.
+ */
+ GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT {
+ RAPIDJSON_ASSERT(this != &rhs);
+ this->~GenericValue();
+ RawAssign(rhs);
+ return *this;
+ }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ //! Move assignment in C++11
+ GenericValue& operator=(GenericValue&& rhs) RAPIDJSON_NOEXCEPT {
+ return *this = rhs.Move();
+ }
+#endif
+
+ //! Assignment of constant string reference (no copy)
+ /*! \param str Constant string reference to be assigned
+ \note This overload is needed to avoid clashes with the generic primitive type assignment overload below.
+ \see GenericStringRef, operator=(T)
+ */
+ GenericValue& operator=(StringRefType str) RAPIDJSON_NOEXCEPT {
+ GenericValue s(str);
+ return *this = s;
+ }
+
+ //! Assignment with primitive types.
+ /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t
+ \param value The value to be assigned.
+
+ \note The source type \c T explicitly disallows all pointer types,
+ especially (\c const) \ref Ch*. This helps avoiding implicitly
+ referencing character strings with insufficient lifetime, use
+ \ref SetString(const Ch*, Allocator&) (for copying) or
+ \ref StringRef() (to explicitly mark the pointer as constant) instead.
+ All other pointer types would implicitly convert to \c bool,
+ use \ref SetBool() instead.
+ */
+ template <typename T>
+ RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer<T>), (GenericValue&))
+ operator=(T value) {
+ GenericValue v(value);
+ return *this = v;
+ }
+
+ //! Deep-copy assignment from Value
+ /*! Assigns a \b copy of the Value to the current Value object
+ \tparam SourceAllocator Allocator type of \c rhs
+ \param rhs Value to copy from (read-only)
+ \param allocator Allocator to use for copying
+ */
+ template <typename SourceAllocator>
+ GenericValue& CopyFrom(const GenericValue<Encoding, SourceAllocator>& rhs, Allocator& allocator) {
+ RAPIDJSON_ASSERT(static_cast<void*>(this) != static_cast<void const*>(&rhs));
+ this->~GenericValue();
+ new (this) GenericValue(rhs, allocator);
+ return *this;
+ }
+
+ //! Exchange the contents of this value with those of other.
+ /*!
+ \param other Another value.
+ \note Constant complexity.
+ */
+ GenericValue& Swap(GenericValue& other) RAPIDJSON_NOEXCEPT {
+ GenericValue temp;
+ temp.RawAssign(*this);
+ RawAssign(other);
+ other.RawAssign(temp);
+ return *this;
+ }
+
+ //! free-standing swap function helper
+ /*!
+ Helper function to enable support for common swap implementation pattern based on \c std::swap:
+ \code
+ void swap(MyClass& a, MyClass& b) {
+ using std::swap;
+ swap(a.value, b.value);
+ // ...
+ }
+ \endcode
+ \see Swap()
+ */
+ friend inline void swap(GenericValue& a, GenericValue& b) RAPIDJSON_NOEXCEPT { a.Swap(b); }
+
+ //! Prepare Value for move semantics
+ /*! \return *this */
+ GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; }
+ //@}
+
+ //!@name Equal-to and not-equal-to operators
+ //@{
+ //! Equal-to operator
+ /*!
+ \note If an object contains duplicated named member, comparing equality with any object is always \c false.
+ \note Linear time complexity (number of all values in the subtree and total lengths of all strings).
+ */
+ template <typename SourceAllocator>
+ bool operator==(const GenericValue<Encoding, SourceAllocator>& rhs) const {
+ typedef GenericValue<Encoding, SourceAllocator> RhsType;
+ if (GetType() != rhs.GetType())
+ return false;
+
+ switch (GetType()) {
+ case kObjectType: // Warning: O(n^2) inner-loop
+ if (data_.o.size != rhs.data_.o.size)
+ return false;
+ for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) {
+ typename RhsType::ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name);
+ if (rhsMemberItr == rhs.MemberEnd() || lhsMemberItr->value != rhsMemberItr->value)
+ return false;
+ }
+ return true;
+
+ case kArrayType:
+ if (data_.a.size != rhs.data_.a.size)
+ return false;
+ for (SizeType i = 0; i < data_.a.size; i++)
+ if ((*this)[i] != rhs[i])
+ return false;
+ return true;
+
+ case kStringType:
+ return StringEqual(rhs);
+
+ case kNumberType:
+ if (IsDouble() || rhs.IsDouble()) {
+ double a = GetDouble(); // May convert from integer to double.
+ double b = rhs.GetDouble(); // Ditto
+ return a >= b && a <= b; // Prevent -Wfloat-equal
+ }
+ else
+ return data_.n.u64 == rhs.data_.n.u64;
+
+ default:
+ return true;
+ }
+ }
+
+ //! Equal-to operator with const C-string pointer
+ bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Equal-to operator with string object
+ /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING.
+ */
+ bool operator==(const std::basic_string<Ch>& rhs) const { return *this == GenericValue(StringRef(rhs)); }
+#endif
+
+ //! Equal-to operator with primitive types
+ /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false
+ */
+ template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>,internal::IsGenericValue<T> >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); }
+
+ //! Not-equal-to operator
+ /*! \return !(*this == rhs)
+ */
+ template <typename SourceAllocator>
+ bool operator!=(const GenericValue<Encoding, SourceAllocator>& rhs) const { return !(*this == rhs); }
+
+ //! Not-equal-to operator with const C-string pointer
+ bool operator!=(const Ch* rhs) const { return !(*this == rhs); }
+
+ //! Not-equal-to operator with arbitrary types
+ /*! \return !(*this == rhs)
+ */
+ template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue<T>), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); }
+
+ //! Equal-to operator with arbitrary types (symmetric version)
+ /*! \return (rhs == lhs)
+ */
+ template <typename T> friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue<T>), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; }
+
+ //! Not-Equal-to operator with arbitrary types (symmetric version)
+ /*! \return !(rhs == lhs)
+ */
+ template <typename T> friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue<T>), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); }
+ //@}
+
+ //!@name Type
+ //@{
+
+ Type GetType() const { return static_cast<Type>(data_.f.flags & kTypeMask); }
+ bool IsNull() const { return data_.f.flags == kNullFlag; }
+ bool IsFalse() const { return data_.f.flags == kFalseFlag; }
+ bool IsTrue() const { return data_.f.flags == kTrueFlag; }
+ bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; }
+ bool IsObject() const { return data_.f.flags == kObjectFlag; }
+ bool IsArray() const { return data_.f.flags == kArrayFlag; }
+ bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; }
+ bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; }
+ bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; }
+ bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; }
+ bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; }
+ bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; }
+ bool IsString() const { return (data_.f.flags & kStringFlag) != 0; }
+
+ // Checks whether a number can be losslessly converted to a double.
+ bool IsLosslessDouble() const {
+ if (!IsNumber()) return false;
+ if (IsUint64()) {
+ uint64_t u = GetUint64();
+ volatile double d = static_cast<double>(u);
+ return (d >= 0.0)
+ && (d < static_cast<double>(std::numeric_limits<uint64_t>::max()))
+ && (u == static_cast<uint64_t>(d));
+ }
+ if (IsInt64()) {
+ int64_t i = GetInt64();
+ volatile double d = static_cast<double>(i);
+ return (d >= static_cast<double>(std::numeric_limits<int64_t>::min()))
+ && (d < static_cast<double>(std::numeric_limits<int64_t>::max()))
+ && (i == static_cast<int64_t>(d));
+ }
+ return true; // double, int, uint are always lossless
+ }
+
+ // Checks whether a number is a float (possible lossy).
+ bool IsFloat() const {
+ if ((data_.f.flags & kDoubleFlag) == 0)
+ return false;
+ double d = GetDouble();
+ return d >= -3.4028234e38 && d <= 3.4028234e38;
+ }
+ // Checks whether a number can be losslessly converted to a float.
+ bool IsLosslessFloat() const {
+ if (!IsNumber()) return false;
+ double a = GetDouble();
+ if (a < static_cast<double>(-std::numeric_limits<float>::max())
+ || a > static_cast<double>(std::numeric_limits<float>::max()))
+ return false;
+ double b = static_cast<double>(static_cast<float>(a));
+ return a >= b && a <= b; // Prevent -Wfloat-equal
+ }
+
+ //@}
+
+ //!@name Null
+ //@{
+
+ GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; }
+
+ //@}
+
+ //!@name Bool
+ //@{
+
+ bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return data_.f.flags == kTrueFlag; }
+ //!< Set boolean value
+ /*! \post IsBool() == true */
+ GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; }
+
+ //@}
+
+ //!@name Object
+ //@{
+
+ //! Set this value as an empty object.
+ /*! \post IsObject() == true */
+ GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; }
+
+ //! Get the number of members in the object.
+ SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; }
+
+ //! Check whether the object is empty.
+ bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; }
+
+ //! Get a value from an object associated with the name.
+ /*! \pre IsObject() == true
+ \tparam T Either \c Ch or \c const \c Ch (template used for disambiguation with \ref operator[](SizeType))
+ \note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7.
+ Since 0.2, if the name is not correct, it will assert.
+ If user is unsure whether a member exists, user should use HasMember() first.
+ A better approach is to use FindMember().
+ \note Linear time complexity.
+ */
+ template <typename T>
+ RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr<internal::IsSame<typename internal::RemoveConst<T>::Type, Ch> >),(GenericValue&)) operator[](T* name) {
+ GenericValue n(StringRef(name));
+ return (*this)[n];
+ }
+ template <typename T>
+ RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr<internal::IsSame<typename internal::RemoveConst<T>::Type, Ch> >),(const GenericValue&)) operator[](T* name) const { return const_cast<GenericValue&>(*this)[name]; }
+
+ //! Get a value from an object associated with the name.
+ /*! \pre IsObject() == true
+ \tparam SourceAllocator Allocator of the \c name value
+
+ \note Compared to \ref operator[](T*), this version is faster because it does not need a StrLen().
+ And it can also handle strings with embedded null characters.
+
+ \note Linear time complexity.
+ */
+ template <typename SourceAllocator>
+ GenericValue& operator[](const GenericValue<Encoding, SourceAllocator>& name) {
+ MemberIterator member = FindMember(name);
+ if (member != MemberEnd())
+ return member->value;
+ else {
+ RAPIDJSON_ASSERT(false); // see above note
+
+ // This will generate -Wexit-time-destructors in clang
+ // static GenericValue NullValue;
+ // return NullValue;
+
+ // Use static buffer and placement-new to prevent destruction
+ static char buffer[sizeof(GenericValue)];
+ return *new (buffer) GenericValue();
+ }
+ }
+ template <typename SourceAllocator>
+ const GenericValue& operator[](const GenericValue<Encoding, SourceAllocator>& name) const { return const_cast<GenericValue&>(*this)[name]; }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Get a value from an object associated with name (string object).
+ GenericValue& operator[](const std::basic_string<Ch>& name) { return (*this)[GenericValue(StringRef(name))]; }
+ const GenericValue& operator[](const std::basic_string<Ch>& name) const { return (*this)[GenericValue(StringRef(name))]; }
+#endif
+
+ //! Const member iterator
+ /*! \pre IsObject() == true */
+ ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer()); }
+ //! Const \em past-the-end member iterator
+ /*! \pre IsObject() == true */
+ ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer() + data_.o.size); }
+ //! Member iterator
+ /*! \pre IsObject() == true */
+ MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer()); }
+ //! \em Past-the-end member iterator
+ /*! \pre IsObject() == true */
+ MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); }
+
+ //! Check whether a member exists in the object.
+ /*!
+ \param name Member name to be searched.
+ \pre IsObject() == true
+ \return Whether a member with that name exists.
+ \note It is better to use FindMember() directly if you need the obtain the value as well.
+ \note Linear time complexity.
+ */
+ bool HasMember(const Ch* name) const { return FindMember(name) != MemberEnd(); }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Check whether a member exists in the object with string object.
+ /*!
+ \param name Member name to be searched.
+ \pre IsObject() == true
+ \return Whether a member with that name exists.
+ \note It is better to use FindMember() directly if you need the obtain the value as well.
+ \note Linear time complexity.
+ */
+ bool HasMember(const std::basic_string<Ch>& name) const { return FindMember(name) != MemberEnd(); }
+#endif
+
+ //! Check whether a member exists in the object with GenericValue name.
+ /*!
+ This version is faster because it does not need a StrLen(). It can also handle string with null character.
+ \param name Member name to be searched.
+ \pre IsObject() == true
+ \return Whether a member with that name exists.
+ \note It is better to use FindMember() directly if you need the obtain the value as well.
+ \note Linear time complexity.
+ */
+ template <typename SourceAllocator>
+ bool HasMember(const GenericValue<Encoding, SourceAllocator>& name) const { return FindMember(name) != MemberEnd(); }
+
+ //! Find member by name.
+ /*!
+ \param name Member name to be searched.
+ \pre IsObject() == true
+ \return Iterator to member, if it exists.
+ Otherwise returns \ref MemberEnd().
+
+ \note Earlier versions of Rapidjson returned a \c NULL pointer, in case
+ the requested member doesn't exist. For consistency with e.g.
+ \c std::map, this has been changed to MemberEnd() now.
+ \note Linear time complexity.
+ */
+ MemberIterator FindMember(const Ch* name) {
+ GenericValue n(StringRef(name));
+ return FindMember(n);
+ }
+
+ ConstMemberIterator FindMember(const Ch* name) const { return const_cast<GenericValue&>(*this).FindMember(name); }
+
+ //! Find member by name.
+ /*!
+ This version is faster because it does not need a StrLen(). It can also handle string with null character.
+ \param name Member name to be searched.
+ \pre IsObject() == true
+ \return Iterator to member, if it exists.
+ Otherwise returns \ref MemberEnd().
+
+ \note Earlier versions of Rapidjson returned a \c NULL pointer, in case
+ the requested member doesn't exist. For consistency with e.g.
+ \c std::map, this has been changed to MemberEnd() now.
+ \note Linear time complexity.
+ */
+ template <typename SourceAllocator>
+ MemberIterator FindMember(const GenericValue<Encoding, SourceAllocator>& name) {
+ RAPIDJSON_ASSERT(IsObject());
+ RAPIDJSON_ASSERT(name.IsString());
+ MemberIterator member = MemberBegin();
+ for ( ; member != MemberEnd(); ++member)
+ if (name.StringEqual(member->name))
+ break;
+ return member;
+ }
+ template <typename SourceAllocator> ConstMemberIterator FindMember(const GenericValue<Encoding, SourceAllocator>& name) const { return const_cast<GenericValue&>(*this).FindMember(name); }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Find member by string object name.
+ /*!
+ \param name Member name to be searched.
+ \pre IsObject() == true
+ \return Iterator to member, if it exists.
+ Otherwise returns \ref MemberEnd().
+ */
+ MemberIterator FindMember(const std::basic_string<Ch>& name) { return FindMember(StringRef(name)); }
+ ConstMemberIterator FindMember(const std::basic_string<Ch>& name) const { return FindMember(StringRef(name)); }
+#endif
+
+ //! Add a member (name-value pair) to the object.
+ /*! \param name A string value as name of member.
+ \param value Value of any type.
+ \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+ \return The value itself for fluent API.
+ \note The ownership of \c name and \c value will be transferred to this object on success.
+ \pre IsObject() && name.IsString()
+ \post name.IsNull() && value.IsNull()
+ \note Amortized Constant time complexity.
+ */
+ GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) {
+ RAPIDJSON_ASSERT(IsObject());
+ RAPIDJSON_ASSERT(name.IsString());
+
+ ObjectData& o = data_.o;
+ if (o.size >= o.capacity) {
+ if (o.capacity == 0) {
+ o.capacity = kDefaultObjectCapacity;
+ SetMembersPointer(reinterpret_cast<Member*>(allocator.Malloc(o.capacity * sizeof(Member))));
+ }
+ else {
+ SizeType oldCapacity = o.capacity;
+ o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5
+ SetMembersPointer(reinterpret_cast<Member*>(allocator.Realloc(GetMembersPointer(), oldCapacity * sizeof(Member), o.capacity * sizeof(Member))));
+ }
+ }
+ Member* members = GetMembersPointer();
+ members[o.size].name.RawAssign(name);
+ members[o.size].value.RawAssign(value);
+ o.size++;
+ return *this;
+ }
+
+ //! Add a constant string value as member (name-value pair) to the object.
+ /*! \param name A string value as name of member.
+ \param value constant string reference as value of member.
+ \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+ \return The value itself for fluent API.
+ \pre IsObject()
+ \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below.
+ \note Amortized Constant time complexity.
+ */
+ GenericValue& AddMember(GenericValue& name, StringRefType value, Allocator& allocator) {
+ GenericValue v(value);
+ return AddMember(name, v, allocator);
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Add a string object as member (name-value pair) to the object.
+ /*! \param name A string value as name of member.
+ \param value constant string reference as value of member.
+ \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+ \return The value itself for fluent API.
+ \pre IsObject()
+ \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below.
+ \note Amortized Constant time complexity.
+ */
+ GenericValue& AddMember(GenericValue& name, std::basic_string<Ch>& value, Allocator& allocator) {
+ GenericValue v(value, allocator);
+ return AddMember(name, v, allocator);
+ }
+#endif
+
+ //! Add any primitive value as member (name-value pair) to the object.
+ /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t
+ \param name A string value as name of member.
+ \param value Value of primitive type \c T as value of member
+ \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator().
+ \return The value itself for fluent API.
+ \pre IsObject()
+
+ \note The source type \c T explicitly disallows all pointer types,
+ especially (\c const) \ref Ch*. This helps avoiding implicitly
+ referencing character strings with insufficient lifetime, use
+ \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref
+ AddMember(StringRefType, StringRefType, Allocator&).
+ All other pointer types would implicitly convert to \c bool,
+ use an explicit cast instead, if needed.
+ \note Amortized Constant time complexity.
+ */
+ template <typename T>
+ RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (GenericValue&))
+ AddMember(GenericValue& name, T value, Allocator& allocator) {
+ GenericValue v(value);
+ return AddMember(name, v, allocator);
+ }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) {
+ return AddMember(name, value, allocator);
+ }
+ GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) {
+ return AddMember(name, value, allocator);
+ }
+ GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) {
+ return AddMember(name, value, allocator);
+ }
+ GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) {
+ GenericValue n(name);
+ return AddMember(n, value, allocator);
+ }
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+
+ //! Add a member (name-value pair) to the object.
+ /*! \param name A constant string reference as name of member.
+ \param value Value of any type.
+ \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+ \return The value itself for fluent API.
+ \note The ownership of \c value will be transferred to this object on success.
+ \pre IsObject()
+ \post value.IsNull()
+ \note Amortized Constant time complexity.
+ */
+ GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) {
+ GenericValue n(name);
+ return AddMember(n, value, allocator);
+ }
+
+ //! Add a constant string value as member (name-value pair) to the object.
+ /*! \param name A constant string reference as name of member.
+ \param value constant string reference as value of member.
+ \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+ \return The value itself for fluent API.
+ \pre IsObject()
+ \note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below.
+ \note Amortized Constant time complexity.
+ */
+ GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) {
+ GenericValue v(value);
+ return AddMember(name, v, allocator);
+ }
+
+ //! Add any primitive value as member (name-value pair) to the object.
+ /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t
+ \param name A constant string reference as name of member.
+ \param value Value of primitive type \c T as value of member
+ \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator().
+ \return The value itself for fluent API.
+ \pre IsObject()
+
+ \note The source type \c T explicitly disallows all pointer types,
+ especially (\c const) \ref Ch*. This helps avoiding implicitly
+ referencing character strings with insufficient lifetime, use
+ \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref
+ AddMember(StringRefType, StringRefType, Allocator&).
+ All other pointer types would implicitly convert to \c bool,
+ use an explicit cast instead, if needed.
+ \note Amortized Constant time complexity.
+ */
+ template <typename T>
+ RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (GenericValue&))
+ AddMember(StringRefType name, T value, Allocator& allocator) {
+ GenericValue n(name);
+ return AddMember(n, value, allocator);
+ }
+
+ //! Remove all members in the object.
+ /*! This function do not deallocate memory in the object, i.e. the capacity is unchanged.
+ \note Linear time complexity.
+ */
+ void RemoveAllMembers() {
+ RAPIDJSON_ASSERT(IsObject());
+ for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m)
+ m->~Member();
+ data_.o.size = 0;
+ }
+
+ //! Remove a member in object by its name.
+ /*! \param name Name of member to be removed.
+ \return Whether the member existed.
+ \note This function may reorder the object members. Use \ref
+ EraseMember(ConstMemberIterator) if you need to preserve the
+ relative order of the remaining members.
+ \note Linear time complexity.
+ */
+ bool RemoveMember(const Ch* name) {
+ GenericValue n(StringRef(name));
+ return RemoveMember(n);
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ bool RemoveMember(const std::basic_string<Ch>& name) { return RemoveMember(GenericValue(StringRef(name))); }
+#endif
+
+ template <typename SourceAllocator>
+ bool RemoveMember(const GenericValue<Encoding, SourceAllocator>& name) {
+ MemberIterator m = FindMember(name);
+ if (m != MemberEnd()) {
+ RemoveMember(m);
+ return true;
+ }
+ else
+ return false;
+ }
+
+ //! Remove a member in object by iterator.
+ /*! \param m member iterator (obtained by FindMember() or MemberBegin()).
+ \return the new iterator after removal.
+ \note This function may reorder the object members. Use \ref
+ EraseMember(ConstMemberIterator) if you need to preserve the
+ relative order of the remaining members.
+ \note Constant time complexity.
+ */
+ MemberIterator RemoveMember(MemberIterator m) {
+ RAPIDJSON_ASSERT(IsObject());
+ RAPIDJSON_ASSERT(data_.o.size > 0);
+ RAPIDJSON_ASSERT(GetMembersPointer() != 0);
+ RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd());
+
+ MemberIterator last(GetMembersPointer() + (data_.o.size - 1));
+ if (data_.o.size > 1 && m != last)
+ *m = *last; // Move the last one to this place
+ else
+ m->~Member(); // Only one left, just destroy
+ --data_.o.size;
+ return m;
+ }
+
+ //! Remove a member from an object by iterator.
+ /*! \param pos iterator to the member to remove
+ \pre IsObject() == true && \ref MemberBegin() <= \c pos < \ref MemberEnd()
+ \return Iterator following the removed element.
+ If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned.
+ \note This function preserves the relative order of the remaining object
+ members. If you do not need this, use the more efficient \ref RemoveMember(MemberIterator).
+ \note Linear time complexity.
+ */
+ MemberIterator EraseMember(ConstMemberIterator pos) {
+ return EraseMember(pos, pos +1);
+ }
+
+ //! Remove members in the range [first, last) from an object.
+ /*! \param first iterator to the first member to remove
+ \param last iterator following the last member to remove
+ \pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd()
+ \return Iterator following the last removed element.
+ \note This function preserves the relative order of the remaining object
+ members.
+ \note Linear time complexity.
+ */
+ MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) {
+ RAPIDJSON_ASSERT(IsObject());
+ RAPIDJSON_ASSERT(data_.o.size > 0);
+ RAPIDJSON_ASSERT(GetMembersPointer() != 0);
+ RAPIDJSON_ASSERT(first >= MemberBegin());
+ RAPIDJSON_ASSERT(first <= last);
+ RAPIDJSON_ASSERT(last <= MemberEnd());
+
+ MemberIterator pos = MemberBegin() + (first - MemberBegin());
+ for (MemberIterator itr = pos; itr != last; ++itr)
+ itr->~Member();
+ std::memmove(&*pos, &*last, static_cast<size_t>(MemberEnd() - last) * sizeof(Member));
+ data_.o.size -= static_cast<SizeType>(last - first);
+ return pos;
+ }
+
+ //! Erase a member in object by its name.
+ /*! \param name Name of member to be removed.
+ \return Whether the member existed.
+ \note Linear time complexity.
+ */
+ bool EraseMember(const Ch* name) {
+ GenericValue n(StringRef(name));
+ return EraseMember(n);
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ bool EraseMember(const std::basic_string<Ch>& name) { return EraseMember(GenericValue(StringRef(name))); }
+#endif
+
+ template <typename SourceAllocator>
+ bool EraseMember(const GenericValue<Encoding, SourceAllocator>& name) {
+ MemberIterator m = FindMember(name);
+ if (m != MemberEnd()) {
+ EraseMember(m);
+ return true;
+ }
+ else
+ return false;
+ }
+
+ Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); }
+ ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); }
+
+ //@}
+
+ //!@name Array
+ //@{
+
+ //! Set this value as an empty array.
+ /*! \post IsArray == true */
+ GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; }
+
+ //! Get the number of elements in array.
+ SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; }
+
+ //! Get the capacity of array.
+ SizeType Capacity() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.capacity; }
+
+ //! Check whether the array is empty.
+ bool Empty() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size == 0; }
+
+ //! Remove all elements in the array.
+ /*! This function do not deallocate memory in the array, i.e. the capacity is unchanged.
+ \note Linear time complexity.
+ */
+ void Clear() {
+ RAPIDJSON_ASSERT(IsArray());
+ GenericValue* e = GetElementsPointer();
+ for (GenericValue* v = e; v != e + data_.a.size; ++v)
+ v->~GenericValue();
+ data_.a.size = 0;
+ }
+
+ //! Get an element from array by index.
+ /*! \pre IsArray() == true
+ \param index Zero-based index of element.
+ \see operator[](T*)
+ */
+ GenericValue& operator[](SizeType index) {
+ RAPIDJSON_ASSERT(IsArray());
+ RAPIDJSON_ASSERT(index < data_.a.size);
+ return GetElementsPointer()[index];
+ }
+ const GenericValue& operator[](SizeType index) const { return const_cast<GenericValue&>(*this)[index]; }
+
+ //! Element iterator
+ /*! \pre IsArray() == true */
+ ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer(); }
+ //! \em Past-the-end element iterator
+ /*! \pre IsArray() == true */
+ ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer() + data_.a.size; }
+ //! Constant element iterator
+ /*! \pre IsArray() == true */
+ ConstValueIterator Begin() const { return const_cast<GenericValue&>(*this).Begin(); }
+ //! Constant \em past-the-end element iterator
+ /*! \pre IsArray() == true */
+ ConstValueIterator End() const { return const_cast<GenericValue&>(*this).End(); }
+
+ //! Request the array to have enough capacity to store elements.
+ /*! \param newCapacity The capacity that the array at least need to have.
+ \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+ \return The value itself for fluent API.
+ \note Linear time complexity.
+ */
+ GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) {
+ RAPIDJSON_ASSERT(IsArray());
+ if (newCapacity > data_.a.capacity) {
+ SetElementsPointer(reinterpret_cast<GenericValue*>(allocator.Realloc(GetElementsPointer(), data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue))));
+ data_.a.capacity = newCapacity;
+ }
+ return *this;
+ }
+
+ //! Append a GenericValue at the end of the array.
+ /*! \param value Value to be appended.
+ \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+ \pre IsArray() == true
+ \post value.IsNull() == true
+ \return The value itself for fluent API.
+ \note The ownership of \c value will be transferred to this array on success.
+ \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient.
+ \note Amortized constant time complexity.
+ */
+ GenericValue& PushBack(GenericValue& value, Allocator& allocator) {
+ RAPIDJSON_ASSERT(IsArray());
+ if (data_.a.size >= data_.a.capacity)
+ Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator);
+ GetElementsPointer()[data_.a.size++].RawAssign(value);
+ return *this;
+ }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ GenericValue& PushBack(GenericValue&& value, Allocator& allocator) {
+ return PushBack(value, allocator);
+ }
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+ //! Append a constant string reference at the end of the array.
+ /*! \param value Constant string reference to be appended.
+ \param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator().
+ \pre IsArray() == true
+ \return The value itself for fluent API.
+ \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient.
+ \note Amortized constant time complexity.
+ \see GenericStringRef
+ */
+ GenericValue& PushBack(StringRefType value, Allocator& allocator) {
+ return (*this).template PushBack<StringRefType>(value, allocator);
+ }
+
+ //! Append a primitive value at the end of the array.
+ /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t
+ \param value Value of primitive type T to be appended.
+ \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+ \pre IsArray() == true
+ \return The value itself for fluent API.
+ \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient.
+
+ \note The source type \c T explicitly disallows all pointer types,
+ especially (\c const) \ref Ch*. This helps avoiding implicitly
+ referencing character strings with insufficient lifetime, use
+ \ref PushBack(GenericValue&, Allocator&) or \ref
+ PushBack(StringRefType, Allocator&).
+ All other pointer types would implicitly convert to \c bool,
+ use an explicit cast instead, if needed.
+ \note Amortized constant time complexity.
+ */
+ template <typename T>
+ RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (GenericValue&))
+ PushBack(T value, Allocator& allocator) {
+ GenericValue v(value);
+ return PushBack(v, allocator);
+ }
+
+ //! Remove the last element in the array.
+ /*!
+ \note Constant time complexity.
+ */
+ GenericValue& PopBack() {
+ RAPIDJSON_ASSERT(IsArray());
+ RAPIDJSON_ASSERT(!Empty());
+ GetElementsPointer()[--data_.a.size].~GenericValue();
+ return *this;
+ }
+
+ //! Remove an element of array by iterator.
+ /*!
+ \param pos iterator to the element to remove
+ \pre IsArray() == true && \ref Begin() <= \c pos < \ref End()
+ \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned.
+ \note Linear time complexity.
+ */
+ ValueIterator Erase(ConstValueIterator pos) {
+ return Erase(pos, pos + 1);
+ }
+
+ //! Remove elements in the range [first, last) of the array.
+ /*!
+ \param first iterator to the first element to remove
+ \param last iterator following the last element to remove
+ \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End()
+ \return Iterator following the last removed element.
+ \note Linear time complexity.
+ */
+ ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) {
+ RAPIDJSON_ASSERT(IsArray());
+ RAPIDJSON_ASSERT(data_.a.size > 0);
+ RAPIDJSON_ASSERT(GetElementsPointer() != 0);
+ RAPIDJSON_ASSERT(first >= Begin());
+ RAPIDJSON_ASSERT(first <= last);
+ RAPIDJSON_ASSERT(last <= End());
+ ValueIterator pos = Begin() + (first - Begin());
+ for (ValueIterator itr = pos; itr != last; ++itr)
+ itr->~GenericValue();
+ std::memmove(pos, last, static_cast<size_t>(End() - last) * sizeof(GenericValue));
+ data_.a.size -= static_cast<SizeType>(last - first);
+ return pos;
+ }
+
+ Array GetArray() { RAPIDJSON_ASSERT(IsArray()); return Array(*this); }
+ ConstArray GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ConstArray(*this); }
+
+ //@}
+
+ //!@name Number
+ //@{
+
+ int GetInt() const { RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; }
+ unsigned GetUint() const { RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; }
+ int64_t GetInt64() const { RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; }
+ uint64_t GetUint64() const { RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; }
+
+ //! Get the value as double type.
+ /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessDouble() to check whether the converison is lossless.
+ */
+ double GetDouble() const {
+ RAPIDJSON_ASSERT(IsNumber());
+ if ((data_.f.flags & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion.
+ if ((data_.f.flags & kIntFlag) != 0) return data_.n.i.i; // int -> double
+ if ((data_.f.flags & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double
+ if ((data_.f.flags & kInt64Flag) != 0) return static_cast<double>(data_.n.i64); // int64_t -> double (may lose precision)
+ RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0); return static_cast<double>(data_.n.u64); // uint64_t -> double (may lose precision)
+ }
+
+ //! Get the value as float type.
+ /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessFloat() to check whether the converison is lossless.
+ */
+ float GetFloat() const {
+ return static_cast<float>(GetDouble());
+ }
+
+ GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; }
+ GenericValue& SetUint(unsigned u) { this->~GenericValue(); new (this) GenericValue(u); return *this; }
+ GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; }
+ GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; }
+ GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; }
+ GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(f); return *this; }
+
+ //@}
+
+ //!@name String
+ //@{
+
+ const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); }
+
+ //! Get the length of string.
+ /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength().
+ */
+ SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((data_.f.flags & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); }
+
+ //! Set this value as a string without copying source string.
+ /*! This version has better performance with supplied length, and also support string containing null character.
+ \param s source string pointer.
+ \param length The length of source string, excluding the trailing null terminator.
+ \return The value itself for fluent API.
+ \post IsString() == true && GetString() == s && GetStringLength() == length
+ \see SetString(StringRefType)
+ */
+ GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); }
+
+ //! Set this value as a string without copying source string.
+ /*! \param s source string reference
+ \return The value itself for fluent API.
+ \post IsString() == true && GetString() == s && GetStringLength() == s.length
+ */
+ GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; }
+
+ //! Set this value as a string by copying from source string.
+ /*! This version has better performance with supplied length, and also support string containing null character.
+ \param s source string.
+ \param length The length of source string, excluding the trailing null terminator.
+ \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator().
+ \return The value itself for fluent API.
+ \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length
+ */
+ GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(StringRef(s, length), allocator); return *this; }
+
+ //! Set this value as a string by copying from source string.
+ /*! \param s source string.
+ \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator().
+ \return The value itself for fluent API.
+ \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length
+ */
+ GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Set this value as a string by copying from source string.
+ /*! \param s source string.
+ \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator().
+ \return The value itself for fluent API.
+ \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size()
+ \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING.
+ */
+ GenericValue& SetString(const std::basic_string<Ch>& s, Allocator& allocator) { return SetString(s.data(), SizeType(s.size()), allocator); }
+#endif
+
+ //@}
+
+ //!@name Array
+ //@{
+
+ //! Templated version for checking whether this value is type T.
+ /*!
+ \tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c const \c char*, \c std::basic_string<Ch>
+ */
+ template <typename T>
+ bool Is() const { return internal::TypeHelper<ValueType, T>::Is(*this); }
+
+ template <typename T>
+ T Get() const { return internal::TypeHelper<ValueType, T>::Get(*this); }
+
+ template <typename T>
+ T Get() { return internal::TypeHelper<ValueType, T>::Get(*this); }
+
+ template<typename T>
+ ValueType& Set(const T& data) { return internal::TypeHelper<ValueType, T>::Set(*this, data); }
+
+ template<typename T>
+ ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper<ValueType, T>::Set(*this, data, allocator); }
+
+ //@}
+
+ //! Generate events of this value to a Handler.
+ /*! This function adopts the GoF visitor pattern.
+ Typical usage is to output this JSON value as JSON text via Writer, which is a Handler.
+ It can also be used to deep clone this value via GenericDocument, which is also a Handler.
+ \tparam Handler type of handler.
+ \param handler An object implementing concept Handler.
+ */
+ template <typename Handler>
+ bool Accept(Handler& handler) const {
+ switch(GetType()) {
+ case kNullType: return handler.Null();
+ case kFalseType: return handler.Bool(false);
+ case kTrueType: return handler.Bool(true);
+
+ case kObjectType:
+ if (RAPIDJSON_UNLIKELY(!handler.StartObject()))
+ return false;
+ for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) {
+ RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator.
+ if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.data_.f.flags & kCopyFlag) != 0)))
+ return false;
+ if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler)))
+ return false;
+ }
+ return handler.EndObject(data_.o.size);
+
+ case kArrayType:
+ if (RAPIDJSON_UNLIKELY(!handler.StartArray()))
+ return false;
+ for (const GenericValue* v = Begin(); v != End(); ++v)
+ if (RAPIDJSON_UNLIKELY(!v->Accept(handler)))
+ return false;
+ return handler.EndArray(data_.a.size);
+
+ case kStringType:
+ return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0);
+
+ default:
+ RAPIDJSON_ASSERT(GetType() == kNumberType);
+ if (IsDouble()) return handler.Double(data_.n.d);
+ else if (IsInt()) return handler.Int(data_.n.i.i);
+ else if (IsUint()) return handler.Uint(data_.n.u.u);
+ else if (IsInt64()) return handler.Int64(data_.n.i64);
+ else return handler.Uint64(data_.n.u64);
+ }
+ }
+
+private:
+ template <typename, typename> friend class GenericValue;
+ template <typename, typename, typename> friend class GenericDocument;
+
+ enum {
+ kBoolFlag = 0x0008,
+ kNumberFlag = 0x0010,
+ kIntFlag = 0x0020,
+ kUintFlag = 0x0040,
+ kInt64Flag = 0x0080,
+ kUint64Flag = 0x0100,
+ kDoubleFlag = 0x0200,
+ kStringFlag = 0x0400,
+ kCopyFlag = 0x0800,
+ kInlineStrFlag = 0x1000,
+
+ // Initial flags of different types.
+ kNullFlag = kNullType,
+ kTrueFlag = kTrueType | kBoolFlag,
+ kFalseFlag = kFalseType | kBoolFlag,
+ kNumberIntFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag,
+ kNumberUintFlag = kNumberType | kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag,
+ kNumberInt64Flag = kNumberType | kNumberFlag | kInt64Flag,
+ kNumberUint64Flag = kNumberType | kNumberFlag | kUint64Flag,
+ kNumberDoubleFlag = kNumberType | kNumberFlag | kDoubleFlag,
+ kNumberAnyFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag,
+ kConstStringFlag = kStringType | kStringFlag,
+ kCopyStringFlag = kStringType | kStringFlag | kCopyFlag,
+ kShortStringFlag = kStringType | kStringFlag | kCopyFlag | kInlineStrFlag,
+ kObjectFlag = kObjectType,
+ kArrayFlag = kArrayType,
+
+ kTypeMask = 0x07
+ };
+
+ static const SizeType kDefaultArrayCapacity = 16;
+ static const SizeType kDefaultObjectCapacity = 16;
+
+ struct Flag {
+#if RAPIDJSON_48BITPOINTER_OPTIMIZATION
+ char payload[sizeof(SizeType) * 2 + 6]; // 2 x SizeType + lower 48-bit pointer
+#elif RAPIDJSON_64BIT
+ char payload[sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes
+#else
+ char payload[sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes
+#endif
+ uint16_t flags;
+ };
+
+ struct String {
+ SizeType length;
+ SizeType hashcode; //!< reserved
+ const Ch* str;
+ }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
+
+ // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars
+ // (excluding the terminating zero) and store a value to determine the length of the contained
+ // string in the last character str[LenPos] by storing "MaxSize - length" there. If the string
+ // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as
+ // the string terminator as well. For getting the string length back from that value just use
+ // "MaxSize - str[LenPos]".
+ // This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode,
+ // 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings).
+ struct ShortString {
+ enum { MaxChars = sizeof(static_cast<Flag*>(0)->payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize };
+ Ch str[MaxChars];
+
+ inline static bool Usable(SizeType len) { return (MaxSize >= len); }
+ inline void SetLength(SizeType len) { str[LenPos] = static_cast<Ch>(MaxSize - len); }
+ inline SizeType GetLength() const { return static_cast<SizeType>(MaxSize - str[LenPos]); }
+ }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
+
+ // By using proper binary layout, retrieval of different integer types do not need conversions.
+ union Number {
+#if RAPIDJSON_ENDIAN == RAPIDJSON_LITTLEENDIAN
+ struct I {
+ int i;
+ char padding[4];
+ }i;
+ struct U {
+ unsigned u;
+ char padding2[4];
+ }u;
+#else
+ struct I {
+ char padding[4];
+ int i;
+ }i;
+ struct U {
+ char padding2[4];
+ unsigned u;
+ }u;
+#endif
+ int64_t i64;
+ uint64_t u64;
+ double d;
+ }; // 8 bytes
+
+ struct ObjectData {
+ SizeType size;
+ SizeType capacity;
+ Member* members;
+ }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
+
+ struct ArrayData {
+ SizeType size;
+ SizeType capacity;
+ GenericValue* elements;
+ }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
+
+ union Data {
+ String s;
+ ShortString ss;
+ Number n;
+ ObjectData o;
+ ArrayData a;
+ Flag f;
+ }; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION
+
+ RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); }
+ RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); }
+ RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); }
+ RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); }
+ RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); }
+ RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); }
+
+ // Initialize this value as array with initial data, without calling destructor.
+ void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) {
+ data_.f.flags = kArrayFlag;
+ if (count) {
+ GenericValue* e = static_cast<GenericValue*>(allocator.Malloc(count * sizeof(GenericValue)));
+ SetElementsPointer(e);
+ std::memcpy(e, values, count * sizeof(GenericValue));
+ }
+ else
+ SetElementsPointer(0);
+ data_.a.size = data_.a.capacity = count;
+ }
+
+ //! Initialize this value as object with initial data, without calling destructor.
+ void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) {
+ data_.f.flags = kObjectFlag;
+ if (count) {
+ Member* m = static_cast<Member*>(allocator.Malloc(count * sizeof(Member)));
+ SetMembersPointer(m);
+ std::memcpy(m, members, count * sizeof(Member));
+ }
+ else
+ SetMembersPointer(0);
+ data_.o.size = data_.o.capacity = count;
+ }
+
+ //! Initialize this value as constant string, without calling destructor.
+ void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT {
+ data_.f.flags = kConstStringFlag;
+ SetStringPointer(s);
+ data_.s.length = s.length;
+ }
+
+ //! Initialize this value as copy string with initial data, without calling destructor.
+ void SetStringRaw(StringRefType s, Allocator& allocator) {
+ Ch* str = 0;
+ if (ShortString::Usable(s.length)) {
+ data_.f.flags = kShortStringFlag;
+ data_.ss.SetLength(s.length);
+ str = data_.ss.str;
+ } else {
+ data_.f.flags = kCopyStringFlag;
+ data_.s.length = s.length;
+ str = static_cast<Ch *>(allocator.Malloc((s.length + 1) * sizeof(Ch)));
+ SetStringPointer(str);
+ }
+ std::memcpy(str, s, s.length * sizeof(Ch));
+ str[s.length] = '\0';
+ }
+
+ //! Assignment without calling destructor
+ void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT {
+ data_ = rhs.data_;
+ // data_.f.flags = rhs.data_.f.flags;
+ rhs.data_.f.flags = kNullFlag;
+ }
+
+ template <typename SourceAllocator>
+ bool StringEqual(const GenericValue<Encoding, SourceAllocator>& rhs) const {
+ RAPIDJSON_ASSERT(IsString());
+ RAPIDJSON_ASSERT(rhs.IsString());
+
+ const SizeType len1 = GetStringLength();
+ const SizeType len2 = rhs.GetStringLength();
+ if(len1 != len2) { return false; }
+
+ const Ch* const str1 = GetString();
+ const Ch* const str2 = rhs.GetString();
+ if(str1 == str2) { return true; } // fast path for constant string
+
+ return (std::memcmp(str1, str2, sizeof(Ch) * len1) == 0);
+ }
+
+ Data data_;
+};
+
+//! GenericValue with UTF8 encoding
+typedef GenericValue<UTF8<> > Value;
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericDocument
+
+//! A document for parsing JSON text as DOM.
+/*!
+ \note implements Handler concept
+ \tparam Encoding Encoding for both parsing and string storage.
+ \tparam Allocator Allocator for allocating memory for the DOM
+ \tparam StackAllocator Allocator for allocating memory for stack during parsing.
+ \warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructor. To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue.
+*/
+template <typename Encoding, typename Allocator = MemoryPoolAllocator<>, typename StackAllocator = CrtAllocator>
+class GenericDocument : public GenericValue<Encoding, Allocator> {
+public:
+ typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding.
+ typedef GenericValue<Encoding, Allocator> ValueType; //!< Value type of the document.
+ typedef Allocator AllocatorType; //!< Allocator type from template parameter.
+
+ //! Constructor
+ /*! Creates an empty document of specified type.
+ \param type Mandatory type of object to create.
+ \param allocator Optional allocator for allocating memory.
+ \param stackCapacity Optional initial capacity of stack in bytes.
+ \param stackAllocator Optional allocator for allocating memory for stack.
+ */
+ explicit GenericDocument(Type type, Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) :
+ GenericValue<Encoding, Allocator>(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_()
+ {
+ if (!allocator_)
+ ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
+ }
+
+ //! Constructor
+ /*! Creates an empty document which type is Null.
+ \param allocator Optional allocator for allocating memory.
+ \param stackCapacity Optional initial capacity of stack in bytes.
+ \param stackAllocator Optional allocator for allocating memory for stack.
+ */
+ GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) :
+ allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_()
+ {
+ if (!allocator_)
+ ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
+ }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ //! Move constructor in C++11
+ GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT
+ : ValueType(std::forward<ValueType>(rhs)), // explicit cast to avoid prohibited move from Document
+ allocator_(rhs.allocator_),
+ ownAllocator_(rhs.ownAllocator_),
+ stack_(std::move(rhs.stack_)),
+ parseResult_(rhs.parseResult_)
+ {
+ rhs.allocator_ = 0;
+ rhs.ownAllocator_ = 0;
+ rhs.parseResult_ = ParseResult();
+ }
+#endif
+
+ ~GenericDocument() {
+ Destroy();
+ }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ //! Move assignment in C++11
+ GenericDocument& operator=(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT
+ {
+ // The cast to ValueType is necessary here, because otherwise it would
+ // attempt to call GenericValue's templated assignment operator.
+ ValueType::operator=(std::forward<ValueType>(rhs));
+
+ // Calling the destructor here would prematurely call stack_'s destructor
+ Destroy();
+
+ allocator_ = rhs.allocator_;
+ ownAllocator_ = rhs.ownAllocator_;
+ stack_ = std::move(rhs.stack_);
+ parseResult_ = rhs.parseResult_;
+
+ rhs.allocator_ = 0;
+ rhs.ownAllocator_ = 0;
+ rhs.parseResult_ = ParseResult();
+
+ return *this;
+ }
+#endif
+
+ //! Exchange the contents of this document with those of another.
+ /*!
+ \param rhs Another document.
+ \note Constant complexity.
+ \see GenericValue::Swap
+ */
+ GenericDocument& Swap(GenericDocument& rhs) RAPIDJSON_NOEXCEPT {
+ ValueType::Swap(rhs);
+ stack_.Swap(rhs.stack_);
+ internal::Swap(allocator_, rhs.allocator_);
+ internal::Swap(ownAllocator_, rhs.ownAllocator_);
+ internal::Swap(parseResult_, rhs.parseResult_);
+ return *this;
+ }
+
+ //! free-standing swap function helper
+ /*!
+ Helper function to enable support for common swap implementation pattern based on \c std::swap:
+ \code
+ void swap(MyClass& a, MyClass& b) {
+ using std::swap;
+ swap(a.doc, b.doc);
+ // ...
+ }
+ \endcode
+ \see Swap()
+ */
+ friend inline void swap(GenericDocument& a, GenericDocument& b) RAPIDJSON_NOEXCEPT { a.Swap(b); }
+
+ //! Populate this document by a generator which produces SAX events.
+ /*! \tparam Generator A functor with <tt>bool f(Handler)</tt> prototype.
+ \param g Generator functor which sends SAX events to the parameter.
+ \return The document itself for fluent API.
+ */
+ template <typename Generator>
+ GenericDocument& Populate(Generator& g) {
+ ClearStackOnExit scope(*this);
+ if (g(*this)) {
+ RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object
+ ValueType::operator=(*stack_.template Pop<ValueType>(1));// Move value from stack to document
+ }
+ return *this;
+ }
+
+ //!@name Parse from stream
+ //!@{
+
+ //! Parse JSON text from an input stream (with Encoding conversion)
+ /*! \tparam parseFlags Combination of \ref ParseFlag.
+ \tparam SourceEncoding Encoding of input stream
+ \tparam InputStream Type of input stream, implementing Stream concept
+ \param is Input stream to be parsed.
+ \return The document itself for fluent API.
+ */
+ template <unsigned parseFlags, typename SourceEncoding, typename InputStream>
+ GenericDocument& ParseStream(InputStream& is) {
+ GenericReader<SourceEncoding, Encoding, StackAllocator> reader(
+ stack_.HasAllocator() ? &stack_.GetAllocator() : 0);
+ ClearStackOnExit scope(*this);
+ parseResult_ = reader.template Parse<parseFlags>(is, *this);
+ if (parseResult_) {
+ RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object
+ ValueType::operator=(*stack_.template Pop<ValueType>(1));// Move value from stack to document
+ }
+ return *this;
+ }
+
+ //! Parse JSON text from an input stream
+ /*! \tparam parseFlags Combination of \ref ParseFlag.
+ \tparam InputStream Type of input stream, implementing Stream concept
+ \param is Input stream to be parsed.
+ \return The document itself for fluent API.
+ */
+ template <unsigned parseFlags, typename InputStream>
+ GenericDocument& ParseStream(InputStream& is) {
+ return ParseStream<parseFlags, Encoding, InputStream>(is);
+ }
+
+ //! Parse JSON text from an input stream (with \ref kParseDefaultFlags)
+ /*! \tparam InputStream Type of input stream, implementing Stream concept
+ \param is Input stream to be parsed.
+ \return The document itself for fluent API.
+ */
+ template <typename InputStream>
+ GenericDocument& ParseStream(InputStream& is) {
+ return ParseStream<kParseDefaultFlags, Encoding, InputStream>(is);
+ }
+ //!@}
+
+ //!@name Parse in-place from mutable string
+ //!@{
+
+ //! Parse JSON text from a mutable string
+ /*! \tparam parseFlags Combination of \ref ParseFlag.
+ \param str Mutable zero-terminated string to be parsed.
+ \return The document itself for fluent API.
+ */
+ template <unsigned parseFlags>
+ GenericDocument& ParseInsitu(Ch* str) {
+ GenericInsituStringStream<Encoding> s(str);
+ return ParseStream<parseFlags | kParseInsituFlag>(s);
+ }
+
+ //! Parse JSON text from a mutable string (with \ref kParseDefaultFlags)
+ /*! \param str Mutable zero-terminated string to be parsed.
+ \return The document itself for fluent API.
+ */
+ GenericDocument& ParseInsitu(Ch* str) {
+ return ParseInsitu<kParseDefaultFlags>(str);
+ }
+ //!@}
+
+ //!@name Parse from read-only string
+ //!@{
+
+ //! Parse JSON text from a read-only string (with Encoding conversion)
+ /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag).
+ \tparam SourceEncoding Transcoding from input Encoding
+ \param str Read-only zero-terminated string to be parsed.
+ */
+ template <unsigned parseFlags, typename SourceEncoding>
+ GenericDocument& Parse(const typename SourceEncoding::Ch* str) {
+ RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag));
+ GenericStringStream<SourceEncoding> s(str);
+ return ParseStream<parseFlags, SourceEncoding>(s);
+ }
+
+ //! Parse JSON text from a read-only string
+ /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag).
+ \param str Read-only zero-terminated string to be parsed.
+ */
+ template <unsigned parseFlags>
+ GenericDocument& Parse(const Ch* str) {
+ return Parse<parseFlags, Encoding>(str);
+ }
+
+ //! Parse JSON text from a read-only string (with \ref kParseDefaultFlags)
+ /*! \param str Read-only zero-terminated string to be parsed.
+ */
+ GenericDocument& Parse(const Ch* str) {
+ return Parse<kParseDefaultFlags>(str);
+ }
+
+ template <unsigned parseFlags, typename SourceEncoding>
+ GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) {
+ RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag));
+ MemoryStream ms(static_cast<const char*>(str), length * sizeof(typename SourceEncoding::Ch));
+ EncodedInputStream<SourceEncoding, MemoryStream> is(ms);
+ ParseStream<parseFlags, SourceEncoding>(is);
+ return *this;
+ }
+
+ template <unsigned parseFlags>
+ GenericDocument& Parse(const Ch* str, size_t length) {
+ return Parse<parseFlags, Encoding>(str, length);
+ }
+
+ GenericDocument& Parse(const Ch* str, size_t length) {
+ return Parse<kParseDefaultFlags>(str, length);
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ template <unsigned parseFlags, typename SourceEncoding>
+ GenericDocument& Parse(const std::basic_string<typename SourceEncoding::Ch>& str) {
+ // c_str() is constant complexity according to standard. Should be faster than Parse(const char*, size_t)
+ return Parse<parseFlags, SourceEncoding>(str.c_str());
+ }
+
+ template <unsigned parseFlags>
+ GenericDocument& Parse(const std::basic_string<Ch>& str) {
+ return Parse<parseFlags, Encoding>(str.c_str());
+ }
+
+ GenericDocument& Parse(const std::basic_string<Ch>& str) {
+ return Parse<kParseDefaultFlags>(str);
+ }
+#endif // RAPIDJSON_HAS_STDSTRING
+
+ //!@}
+
+ //!@name Handling parse errors
+ //!@{
+
+ //! Whether a parse error has occured in the last parsing.
+ bool HasParseError() const { return parseResult_.IsError(); }
+
+ //! Get the \ref ParseErrorCode of last parsing.
+ ParseErrorCode GetParseError() const { return parseResult_.Code(); }
+
+ //! Get the position of last parsing error in input, 0 otherwise.
+ size_t GetErrorOffset() const { return parseResult_.Offset(); }
+
+ //! Implicit conversion to get the last parse result
+#ifndef __clang // -Wdocumentation
+ /*! \return \ref ParseResult of the last parse operation
+
+ \code
+ Document doc;
+ ParseResult ok = doc.Parse(json);
+ if (!ok)
+ printf( "JSON parse error: %s (%u)\n", GetParseError_En(ok.Code()), ok.Offset());
+ \endcode
+ */
+#endif
+ operator ParseResult() const { return parseResult_; }
+ //!@}
+
+ //! Get the allocator of this document.
+ Allocator& GetAllocator() {
+ RAPIDJSON_ASSERT(allocator_);
+ return *allocator_;
+ }
+
+ //! Get the capacity of stack in bytes.
+ size_t GetStackCapacity() const { return stack_.GetCapacity(); }
+
+private:
+ // clear stack on any exit from ParseStream, e.g. due to exception
+ struct ClearStackOnExit {
+ explicit ClearStackOnExit(GenericDocument& d) : d_(d) {}
+ ~ClearStackOnExit() { d_.ClearStack(); }
+ private:
+ ClearStackOnExit(const ClearStackOnExit&);
+ ClearStackOnExit& operator=(const ClearStackOnExit&);
+ GenericDocument& d_;
+ };
+
+ // callers of the following private Handler functions
+ // template <typename,typename,typename> friend class GenericReader; // for parsing
+ template <typename, typename> friend class GenericValue; // for deep copying
+
+public:
+ // Implementation of Handler
+ bool Null() { new (stack_.template Push<ValueType>()) ValueType(); return true; }
+ bool Bool(bool b) { new (stack_.template Push<ValueType>()) ValueType(b); return true; }
+ bool Int(int i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; }
+ bool Uint(unsigned i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; }
+ bool Int64(int64_t i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; }
+ bool Uint64(uint64_t i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; }
+ bool Double(double d) { new (stack_.template Push<ValueType>()) ValueType(d); return true; }
+
+ bool RawNumber(const Ch* str, SizeType length, bool copy) {
+ if (copy)
+ new (stack_.template Push<ValueType>()) ValueType(str, length, GetAllocator());
+ else
+ new (stack_.template Push<ValueType>()) ValueType(str, length);
+ return true;
+ }
+
+ bool String(const Ch* str, SizeType length, bool copy) {
+ if (copy)
+ new (stack_.template Push<ValueType>()) ValueType(str, length, GetAllocator());
+ else
+ new (stack_.template Push<ValueType>()) ValueType(str, length);
+ return true;
+ }
+
+ bool StartObject() { new (stack_.template Push<ValueType>()) ValueType(kObjectType); return true; }
+
+ bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); }
+
+ bool EndObject(SizeType memberCount) {
+ typename ValueType::Member* members = stack_.template Pop<typename ValueType::Member>(memberCount);
+ stack_.template Top<ValueType>()->SetObjectRaw(members, memberCount, GetAllocator());
+ return true;
+ }
+
+ bool StartArray() { new (stack_.template Push<ValueType>()) ValueType(kArrayType); return true; }
+
+ bool EndArray(SizeType elementCount) {
+ ValueType* elements = stack_.template Pop<ValueType>(elementCount);
+ stack_.template Top<ValueType>()->SetArrayRaw(elements, elementCount, GetAllocator());
+ return true;
+ }
+
+private:
+ //! Prohibit copying
+ GenericDocument(const GenericDocument&);
+ //! Prohibit assignment
+ GenericDocument& operator=(const GenericDocument&);
+
+ void ClearStack() {
+ if (Allocator::kNeedFree)
+ while (stack_.GetSize() > 0) // Here assumes all elements in stack array are GenericValue (Member is actually 2 GenericValue objects)
+ (stack_.template Pop<ValueType>(1))->~ValueType();
+ else
+ stack_.Clear();
+ stack_.ShrinkToFit();
+ }
+
+ void Destroy() {
+ RAPIDJSON_DELETE(ownAllocator_);
+ }
+
+ static const size_t kDefaultStackCapacity = 1024;
+ Allocator* allocator_;
+ Allocator* ownAllocator_;
+ internal::Stack<StackAllocator> stack_;
+ ParseResult parseResult_;
+};
+
+//! GenericDocument with UTF8 encoding
+typedef GenericDocument<UTF8<> > Document;
+
+// defined here due to the dependency on GenericDocument
+template <typename Encoding, typename Allocator>
+template <typename SourceAllocator>
+inline
+GenericValue<Encoding,Allocator>::GenericValue(const GenericValue<Encoding,SourceAllocator>& rhs, Allocator& allocator)
+{
+ switch (rhs.GetType()) {
+ case kObjectType:
+ case kArrayType: { // perform deep copy via SAX Handler
+ GenericDocument<Encoding,Allocator> d(&allocator);
+ rhs.Accept(d);
+ RawAssign(*d.stack_.template Pop<GenericValue>(1));
+ }
+ break;
+ case kStringType:
+ if (rhs.data_.f.flags == kConstStringFlag) {
+ data_.f.flags = rhs.data_.f.flags;
+ data_ = *reinterpret_cast<const Data*>(&rhs.data_);
+ } else {
+ SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator);
+ }
+ break;
+ default:
+ data_.f.flags = rhs.data_.f.flags;
+ data_ = *reinterpret_cast<const Data*>(&rhs.data_);
+ break;
+ }
+}
+
+//! Helper class for accessing Value of array type.
+/*!
+ Instance of this helper class is obtained by \c GenericValue::GetArray().
+ In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1.
+*/
+template <bool Const, typename ValueT>
+class GenericArray {
+public:
+ typedef GenericArray<true, ValueT> ConstArray;
+ typedef GenericArray<false, ValueT> Array;
+ typedef ValueT PlainType;
+ typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType;
+ typedef ValueType* ValueIterator; // This may be const or non-const iterator
+ typedef const ValueT* ConstValueIterator;
+ typedef typename ValueType::AllocatorType AllocatorType;
+ typedef typename ValueType::StringRefType StringRefType;
+
+ template <typename, typename>
+ friend class GenericValue;
+
+ GenericArray(const GenericArray& rhs) : value_(rhs.value_) {}
+ GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; }
+ ~GenericArray() {}
+
+ SizeType Size() const { return value_.Size(); }
+ SizeType Capacity() const { return value_.Capacity(); }
+ bool Empty() const { return value_.Empty(); }
+ void Clear() const { value_.Clear(); }
+ ValueType& operator[](SizeType index) const { return value_[index]; }
+ ValueIterator Begin() const { return value_.Begin(); }
+ ValueIterator End() const { return value_.End(); }
+ GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { value_.Reserve(newCapacity, allocator); return *this; }
+ GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; }
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; }
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; }
+ template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; }
+ GenericArray PopBack() const { value_.PopBack(); return *this; }
+ ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(pos); }
+ ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return value_.Erase(first, last); }
+
+#if RAPIDJSON_HAS_CXX11_RANGE_FOR
+ ValueIterator begin() const { return value_.Begin(); }
+ ValueIterator end() const { return value_.End(); }
+#endif
+
+private:
+ GenericArray();
+ GenericArray(ValueType& value) : value_(value) {}
+ ValueType& value_;
+};
+
+//! Helper class for accessing Value of object type.
+/*!
+ Instance of this helper class is obtained by \c GenericValue::GetObject().
+ In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1.
+*/
+template <bool Const, typename ValueT>
+class GenericObject {
+public:
+ typedef GenericObject<true, ValueT> ConstObject;
+ typedef GenericObject<false, ValueT> Object;
+ typedef ValueT PlainType;
+ typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType;
+ typedef GenericMemberIterator<Const, typename ValueT::EncodingType, typename ValueT::AllocatorType> MemberIterator; // This may be const or non-const iterator
+ typedef GenericMemberIterator<true, typename ValueT::EncodingType, typename ValueT::AllocatorType> ConstMemberIterator;
+ typedef typename ValueType::AllocatorType AllocatorType;
+ typedef typename ValueType::StringRefType StringRefType;
+ typedef typename ValueType::EncodingType EncodingType;
+ typedef typename ValueType::Ch Ch;
+
+ template <typename, typename>
+ friend class GenericValue;
+
+ GenericObject(const GenericObject& rhs) : value_(rhs.value_) {}
+ GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; }
+ ~GenericObject() {}
+
+ SizeType MemberCount() const { return value_.MemberCount(); }
+ bool ObjectEmpty() const { return value_.ObjectEmpty(); }
+ template <typename T> ValueType& operator[](T* name) const { return value_[name]; }
+ template <typename SourceAllocator> ValueType& operator[](const GenericValue<EncodingType, SourceAllocator>& name) const { return value_[name]; }
+#if RAPIDJSON_HAS_STDSTRING
+ ValueType& operator[](const std::basic_string<Ch>& name) const { return value_[name]; }
+#endif
+ MemberIterator MemberBegin() const { return value_.MemberBegin(); }
+ MemberIterator MemberEnd() const { return value_.MemberEnd(); }
+ bool HasMember(const Ch* name) const { return value_.HasMember(name); }
+#if RAPIDJSON_HAS_STDSTRING
+ bool HasMember(const std::basic_string<Ch>& name) const { return value_.HasMember(name); }
+#endif
+ template <typename SourceAllocator> bool HasMember(const GenericValue<EncodingType, SourceAllocator>& name) const { return value_.HasMember(name); }
+ MemberIterator FindMember(const Ch* name) const { return value_.FindMember(name); }
+ template <typename SourceAllocator> MemberIterator FindMember(const GenericValue<EncodingType, SourceAllocator>& name) const { return value_.FindMember(name); }
+#if RAPIDJSON_HAS_STDSTRING
+ MemberIterator FindMember(const std::basic_string<Ch>& name) const { return value_.FindMember(name); }
+#endif
+ GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+ GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+#if RAPIDJSON_HAS_STDSTRING
+ GenericObject AddMember(ValueType& name, std::basic_string<Ch>& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+#endif
+ template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+ GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+ GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+ GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+ GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+ template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+ void RemoveAllMembers() { return value_.RemoveAllMembers(); }
+ bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); }
+#if RAPIDJSON_HAS_STDSTRING
+ bool RemoveMember(const std::basic_string<Ch>& name) const { return value_.RemoveMember(name); }
+#endif
+ template <typename SourceAllocator> bool RemoveMember(const GenericValue<EncodingType, SourceAllocator>& name) const { return value_.RemoveMember(name); }
+ MemberIterator RemoveMember(MemberIterator m) const { return value_.RemoveMember(m); }
+ MemberIterator EraseMember(ConstMemberIterator pos) const { return value_.EraseMember(pos); }
+ MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return value_.EraseMember(first, last); }
+ bool EraseMember(const Ch* name) const { return value_.EraseMember(name); }
+#if RAPIDJSON_HAS_STDSTRING
+ bool EraseMember(const std::basic_string<Ch>& name) const { return EraseMember(ValueType(StringRef(name))); }
+#endif
+ template <typename SourceAllocator> bool EraseMember(const GenericValue<EncodingType, SourceAllocator>& name) const { return value_.EraseMember(name); }
+
+#if RAPIDJSON_HAS_CXX11_RANGE_FOR
+ MemberIterator begin() const { return value_.MemberBegin(); }
+ MemberIterator end() const { return value_.MemberEnd(); }
+#endif
+
+private:
+ GenericObject();
+ GenericObject(ValueType& value) : value_(value) {}
+ ValueType& value_;
+};
+
+RAPIDJSON_NAMESPACE_END
+RAPIDJSON_DIAG_POP
+
+#endif // RAPIDJSON_DOCUMENT_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/encodedstream.h b/ext/librethinkdbxx/src/rapidjson/encodedstream.h
new file mode 100644
index 00000000..14506838
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/encodedstream.h
@@ -0,0 +1,299 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_ENCODEDSTREAM_H_
+#define RAPIDJSON_ENCODEDSTREAM_H_
+
+#include "stream.h"
+#include "memorystream.h"
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Input byte stream wrapper with a statically bound encoding.
+/*!
+ \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
+ \tparam InputByteStream Type of input byte stream. For example, FileReadStream.
+*/
+template <typename Encoding, typename InputByteStream>
+class EncodedInputStream {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+public:
+ typedef typename Encoding::Ch Ch;
+
+ EncodedInputStream(InputByteStream& is) : is_(is) {
+ current_ = Encoding::TakeBOM(is_);
+ }
+
+ Ch Peek() const { return current_; }
+ Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; }
+ size_t Tell() const { return is_.Tell(); }
+
+ // Not implemented
+ void Put(Ch) { RAPIDJSON_ASSERT(false); }
+ void Flush() { RAPIDJSON_ASSERT(false); }
+ Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+private:
+ EncodedInputStream(const EncodedInputStream&);
+ EncodedInputStream& operator=(const EncodedInputStream&);
+
+ InputByteStream& is_;
+ Ch current_;
+};
+
+//! Specialized for UTF8 MemoryStream.
+template <>
+class EncodedInputStream<UTF8<>, MemoryStream> {
+public:
+ typedef UTF8<>::Ch Ch;
+
+ EncodedInputStream(MemoryStream& is) : is_(is) {
+ if (static_cast<unsigned char>(is_.Peek()) == 0xEFu) is_.Take();
+ if (static_cast<unsigned char>(is_.Peek()) == 0xBBu) is_.Take();
+ if (static_cast<unsigned char>(is_.Peek()) == 0xBFu) is_.Take();
+ }
+ Ch Peek() const { return is_.Peek(); }
+ Ch Take() { return is_.Take(); }
+ size_t Tell() const { return is_.Tell(); }
+
+ // Not implemented
+ void Put(Ch) {}
+ void Flush() {}
+ Ch* PutBegin() { return 0; }
+ size_t PutEnd(Ch*) { return 0; }
+
+ MemoryStream& is_;
+
+private:
+ EncodedInputStream(const EncodedInputStream&);
+ EncodedInputStream& operator=(const EncodedInputStream&);
+};
+
+//! Output byte stream wrapper with statically bound encoding.
+/*!
+ \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
+ \tparam OutputByteStream Type of input byte stream. For example, FileWriteStream.
+*/
+template <typename Encoding, typename OutputByteStream>
+class EncodedOutputStream {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+public:
+ typedef typename Encoding::Ch Ch;
+
+ EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) {
+ if (putBOM)
+ Encoding::PutBOM(os_);
+ }
+
+ void Put(Ch c) { Encoding::Put(os_, c); }
+ void Flush() { os_.Flush(); }
+
+ // Not implemented
+ Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;}
+ Ch Take() { RAPIDJSON_ASSERT(false); return 0;}
+ size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
+ Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+private:
+ EncodedOutputStream(const EncodedOutputStream&);
+ EncodedOutputStream& operator=(const EncodedOutputStream&);
+
+ OutputByteStream& os_;
+};
+
+#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
+
+//! Input stream wrapper with dynamically bound encoding and automatic encoding detection.
+/*!
+ \tparam CharType Type of character for reading.
+ \tparam InputByteStream type of input byte stream to be wrapped.
+*/
+template <typename CharType, typename InputByteStream>
+class AutoUTFInputStream {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+public:
+ typedef CharType Ch;
+
+ //! Constructor.
+ /*!
+ \param is input stream to be wrapped.
+ \param type UTF encoding type if it is not detected from the stream.
+ */
+ AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) {
+ RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
+ DetectType();
+ static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) };
+ takeFunc_ = f[type_];
+ current_ = takeFunc_(*is_);
+ }
+
+ UTFType GetType() const { return type_; }
+ bool HasBOM() const { return hasBOM_; }
+
+ Ch Peek() const { return current_; }
+ Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; }
+ size_t Tell() const { return is_->Tell(); }
+
+ // Not implemented
+ void Put(Ch) { RAPIDJSON_ASSERT(false); }
+ void Flush() { RAPIDJSON_ASSERT(false); }
+ Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+private:
+ AutoUTFInputStream(const AutoUTFInputStream&);
+ AutoUTFInputStream& operator=(const AutoUTFInputStream&);
+
+ // Detect encoding type with BOM or RFC 4627
+ void DetectType() {
+ // BOM (Byte Order Mark):
+ // 00 00 FE FF UTF-32BE
+ // FF FE 00 00 UTF-32LE
+ // FE FF UTF-16BE
+ // FF FE UTF-16LE
+ // EF BB BF UTF-8
+
+ const unsigned char* c = reinterpret_cast<const unsigned char *>(is_->Peek4());
+ if (!c)
+ return;
+
+ unsigned bom = static_cast<unsigned>(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24));
+ hasBOM_ = false;
+ if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
+ else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
+ else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); }
+ else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); }
+ else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); }
+
+ // RFC 4627: Section 3
+ // "Since the first two characters of a JSON text will always be ASCII
+ // characters [RFC0020], it is possible to determine whether an octet
+ // stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
+ // at the pattern of nulls in the first four octets."
+ // 00 00 00 xx UTF-32BE
+ // 00 xx 00 xx UTF-16BE
+ // xx 00 00 00 UTF-32LE
+ // xx 00 xx 00 UTF-16LE
+ // xx xx xx xx UTF-8
+
+ if (!hasBOM_) {
+ unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0);
+ switch (pattern) {
+ case 0x08: type_ = kUTF32BE; break;
+ case 0x0A: type_ = kUTF16BE; break;
+ case 0x01: type_ = kUTF32LE; break;
+ case 0x05: type_ = kUTF16LE; break;
+ case 0x0F: type_ = kUTF8; break;
+ default: break; // Use type defined by user.
+ }
+ }
+
+ // Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
+ if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
+ if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
+ }
+
+ typedef Ch (*TakeFunc)(InputByteStream& is);
+ InputByteStream* is_;
+ UTFType type_;
+ Ch current_;
+ TakeFunc takeFunc_;
+ bool hasBOM_;
+};
+
+//! Output stream wrapper with dynamically bound encoding and automatic encoding detection.
+/*!
+ \tparam CharType Type of character for writing.
+ \tparam OutputByteStream type of output byte stream to be wrapped.
+*/
+template <typename CharType, typename OutputByteStream>
+class AutoUTFOutputStream {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+public:
+ typedef CharType Ch;
+
+ //! Constructor.
+ /*!
+ \param os output stream to be wrapped.
+ \param type UTF encoding type.
+ \param putBOM Whether to write BOM at the beginning of the stream.
+ */
+ AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) {
+ RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
+
+ // Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
+ if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
+ if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
+
+ static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) };
+ putFunc_ = f[type_];
+
+ if (putBOM)
+ PutBOM();
+ }
+
+ UTFType GetType() const { return type_; }
+
+ void Put(Ch c) { putFunc_(*os_, c); }
+ void Flush() { os_->Flush(); }
+
+ // Not implemented
+ Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;}
+ Ch Take() { RAPIDJSON_ASSERT(false); return 0;}
+ size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
+ Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+private:
+ AutoUTFOutputStream(const AutoUTFOutputStream&);
+ AutoUTFOutputStream& operator=(const AutoUTFOutputStream&);
+
+ void PutBOM() {
+ typedef void (*PutBOMFunc)(OutputByteStream&);
+ static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) };
+ f[type_](*os_);
+ }
+
+ typedef void (*PutFunc)(OutputByteStream&, Ch);
+
+ OutputByteStream* os_;
+ UTFType type_;
+ PutFunc putFunc_;
+};
+
+#undef RAPIDJSON_ENCODINGS_FUNC
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_FILESTREAM_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/encodings.h b/ext/librethinkdbxx/src/rapidjson/encodings.h
new file mode 100644
index 00000000..baa7c2b1
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/encodings.h
@@ -0,0 +1,716 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_ENCODINGS_H_
+#define RAPIDJSON_ENCODINGS_H_
+
+#include "rapidjson.h"
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data
+RAPIDJSON_DIAG_OFF(4702) // unreachable code
+#elif defined(__GNUC__)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+RAPIDJSON_DIAG_OFF(overflow)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// Encoding
+
+/*! \class rapidjson::Encoding
+ \brief Concept for encoding of Unicode characters.
+
+\code
+concept Encoding {
+ typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition.
+
+ enum { supportUnicode = 1 }; // or 0 if not supporting unicode
+
+ //! \brief Encode a Unicode codepoint to an output stream.
+ //! \param os Output stream.
+ //! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively.
+ template<typename OutputStream>
+ static void Encode(OutputStream& os, unsigned codepoint);
+
+ //! \brief Decode a Unicode codepoint from an input stream.
+ //! \param is Input stream.
+ //! \param codepoint Output of the unicode codepoint.
+ //! \return true if a valid codepoint can be decoded from the stream.
+ template <typename InputStream>
+ static bool Decode(InputStream& is, unsigned* codepoint);
+
+ //! \brief Validate one Unicode codepoint from an encoded stream.
+ //! \param is Input stream to obtain codepoint.
+ //! \param os Output for copying one codepoint.
+ //! \return true if it is valid.
+ //! \note This function just validating and copying the codepoint without actually decode it.
+ template <typename InputStream, typename OutputStream>
+ static bool Validate(InputStream& is, OutputStream& os);
+
+ // The following functions are deal with byte streams.
+
+ //! Take a character from input byte stream, skip BOM if exist.
+ template <typename InputByteStream>
+ static CharType TakeBOM(InputByteStream& is);
+
+ //! Take a character from input byte stream.
+ template <typename InputByteStream>
+ static Ch Take(InputByteStream& is);
+
+ //! Put BOM to output byte stream.
+ template <typename OutputByteStream>
+ static void PutBOM(OutputByteStream& os);
+
+ //! Put a character to output byte stream.
+ template <typename OutputByteStream>
+ static void Put(OutputByteStream& os, Ch c);
+};
+\endcode
+*/
+
+///////////////////////////////////////////////////////////////////////////////
+// UTF8
+
+//! UTF-8 encoding.
+/*! http://en.wikipedia.org/wiki/UTF-8
+ http://tools.ietf.org/html/rfc3629
+ \tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char.
+ \note implements Encoding concept
+*/
+template<typename CharType = char>
+struct UTF8 {
+ typedef CharType Ch;
+
+ enum { supportUnicode = 1 };
+
+ template<typename OutputStream>
+ static void Encode(OutputStream& os, unsigned codepoint) {
+ if (codepoint <= 0x7F)
+ os.Put(static_cast<Ch>(codepoint & 0xFF));
+ else if (codepoint <= 0x7FF) {
+ os.Put(static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
+ os.Put(static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
+ }
+ else if (codepoint <= 0xFFFF) {
+ os.Put(static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
+ os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
+ os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
+ }
+ else {
+ RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
+ os.Put(static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
+ os.Put(static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
+ os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
+ os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
+ }
+ }
+
+ template<typename OutputStream>
+ static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
+ if (codepoint <= 0x7F)
+ PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF));
+ else if (codepoint <= 0x7FF) {
+ PutUnsafe(os, static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
+ PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
+ }
+ else if (codepoint <= 0xFFFF) {
+ PutUnsafe(os, static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
+ PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
+ PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F)));
+ }
+ else {
+ RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
+ PutUnsafe(os, static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
+ PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
+ PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
+ PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F)));
+ }
+ }
+
+ template <typename InputStream>
+ static bool Decode(InputStream& is, unsigned* codepoint) {
+#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast<unsigned char>(c) & 0x3Fu)
+#define TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0)
+#define TAIL() COPY(); TRANS(0x70)
+ typename InputStream::Ch c = is.Take();
+ if (!(c & 0x80)) {
+ *codepoint = static_cast<unsigned char>(c);
+ return true;
+ }
+
+ unsigned char type = GetRange(static_cast<unsigned char>(c));
+ if (type >= 32) {
+ *codepoint = 0;
+ } else {
+ *codepoint = (0xFF >> type) & static_cast<unsigned char>(c);
+ }
+ bool result = true;
+ switch (type) {
+ case 2: TAIL(); return result;
+ case 3: TAIL(); TAIL(); return result;
+ case 4: COPY(); TRANS(0x50); TAIL(); return result;
+ case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result;
+ case 6: TAIL(); TAIL(); TAIL(); return result;
+ case 10: COPY(); TRANS(0x20); TAIL(); return result;
+ case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result;
+ default: return false;
+ }
+#undef COPY
+#undef TRANS
+#undef TAIL
+ }
+
+ template <typename InputStream, typename OutputStream>
+ static bool Validate(InputStream& is, OutputStream& os) {
+#define COPY() os.Put(c = is.Take())
+#define TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0)
+#define TAIL() COPY(); TRANS(0x70)
+ Ch c;
+ COPY();
+ if (!(c & 0x80))
+ return true;
+
+ bool result = true;
+ switch (GetRange(static_cast<unsigned char>(c))) {
+ case 2: TAIL(); return result;
+ case 3: TAIL(); TAIL(); return result;
+ case 4: COPY(); TRANS(0x50); TAIL(); return result;
+ case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result;
+ case 6: TAIL(); TAIL(); TAIL(); return result;
+ case 10: COPY(); TRANS(0x20); TAIL(); return result;
+ case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result;
+ default: return false;
+ }
+#undef COPY
+#undef TRANS
+#undef TAIL
+ }
+
+ static unsigned char GetRange(unsigned char c) {
+ // Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
+ // With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types.
+ static const unsigned char type[] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
+ 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
+ 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
+ 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
+ 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
+ };
+ return type[c];
+ }
+
+ template <typename InputByteStream>
+ static CharType TakeBOM(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ typename InputByteStream::Ch c = Take(is);
+ if (static_cast<unsigned char>(c) != 0xEFu) return c;
+ c = is.Take();
+ if (static_cast<unsigned char>(c) != 0xBBu) return c;
+ c = is.Take();
+ if (static_cast<unsigned char>(c) != 0xBFu) return c;
+ c = is.Take();
+ return c;
+ }
+
+ template <typename InputByteStream>
+ static Ch Take(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ return static_cast<Ch>(is.Take());
+ }
+
+ template <typename OutputByteStream>
+ static void PutBOM(OutputByteStream& os) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ os.Put(static_cast<typename OutputByteStream::Ch>(0xEFu));
+ os.Put(static_cast<typename OutputByteStream::Ch>(0xBBu));
+ os.Put(static_cast<typename OutputByteStream::Ch>(0xBFu));
+ }
+
+ template <typename OutputByteStream>
+ static void Put(OutputByteStream& os, Ch c) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ os.Put(static_cast<typename OutputByteStream::Ch>(c));
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// UTF16
+
+//! UTF-16 encoding.
+/*! http://en.wikipedia.org/wiki/UTF-16
+ http://tools.ietf.org/html/rfc2781
+ \tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead.
+ \note implements Encoding concept
+
+ \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
+ For streaming, use UTF16LE and UTF16BE, which handle endianness.
+*/
+template<typename CharType = wchar_t>
+struct UTF16 {
+ typedef CharType Ch;
+ RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2);
+
+ enum { supportUnicode = 1 };
+
+ template<typename OutputStream>
+ static void Encode(OutputStream& os, unsigned codepoint) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
+ if (codepoint <= 0xFFFF) {
+ RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair
+ os.Put(static_cast<typename OutputStream::Ch>(codepoint));
+ }
+ else {
+ RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
+ unsigned v = codepoint - 0x10000;
+ os.Put(static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
+ os.Put((v & 0x3FF) | 0xDC00);
+ }
+ }
+
+
+ template<typename OutputStream>
+ static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
+ if (codepoint <= 0xFFFF) {
+ RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair
+ PutUnsafe(os, static_cast<typename OutputStream::Ch>(codepoint));
+ }
+ else {
+ RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
+ unsigned v = codepoint - 0x10000;
+ PutUnsafe(os, static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
+ PutUnsafe(os, (v & 0x3FF) | 0xDC00);
+ }
+ }
+
+ template <typename InputStream>
+ static bool Decode(InputStream& is, unsigned* codepoint) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
+ typename InputStream::Ch c = is.Take();
+ if (c < 0xD800 || c > 0xDFFF) {
+ *codepoint = static_cast<unsigned>(c);
+ return true;
+ }
+ else if (c <= 0xDBFF) {
+ *codepoint = (static_cast<unsigned>(c) & 0x3FF) << 10;
+ c = is.Take();
+ *codepoint |= (static_cast<unsigned>(c) & 0x3FF);
+ *codepoint += 0x10000;
+ return c >= 0xDC00 && c <= 0xDFFF;
+ }
+ return false;
+ }
+
+ template <typename InputStream, typename OutputStream>
+ static bool Validate(InputStream& is, OutputStream& os) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
+ typename InputStream::Ch c;
+ os.Put(static_cast<typename OutputStream::Ch>(c = is.Take()));
+ if (c < 0xD800 || c > 0xDFFF)
+ return true;
+ else if (c <= 0xDBFF) {
+ os.Put(c = is.Take());
+ return c >= 0xDC00 && c <= 0xDFFF;
+ }
+ return false;
+ }
+};
+
+//! UTF-16 little endian encoding.
+template<typename CharType = wchar_t>
+struct UTF16LE : UTF16<CharType> {
+ template <typename InputByteStream>
+ static CharType TakeBOM(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ CharType c = Take(is);
+ return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c;
+ }
+
+ template <typename InputByteStream>
+ static CharType Take(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ unsigned c = static_cast<uint8_t>(is.Take());
+ c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
+ return static_cast<CharType>(c);
+ }
+
+ template <typename OutputByteStream>
+ static void PutBOM(OutputByteStream& os) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
+ os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
+ }
+
+ template <typename OutputByteStream>
+ static void Put(OutputByteStream& os, CharType c) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu));
+ os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu));
+ }
+};
+
+//! UTF-16 big endian encoding.
+template<typename CharType = wchar_t>
+struct UTF16BE : UTF16<CharType> {
+ template <typename InputByteStream>
+ static CharType TakeBOM(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ CharType c = Take(is);
+ return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c;
+ }
+
+ template <typename InputByteStream>
+ static CharType Take(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
+ c |= static_cast<uint8_t>(is.Take());
+ return static_cast<CharType>(c);
+ }
+
+ template <typename OutputByteStream>
+ static void PutBOM(OutputByteStream& os) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
+ os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
+ }
+
+ template <typename OutputByteStream>
+ static void Put(OutputByteStream& os, CharType c) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu));
+ os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu));
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// UTF32
+
+//! UTF-32 encoding.
+/*! http://en.wikipedia.org/wiki/UTF-32
+ \tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead.
+ \note implements Encoding concept
+
+ \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
+ For streaming, use UTF32LE and UTF32BE, which handle endianness.
+*/
+template<typename CharType = unsigned>
+struct UTF32 {
+ typedef CharType Ch;
+ RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4);
+
+ enum { supportUnicode = 1 };
+
+ template<typename OutputStream>
+ static void Encode(OutputStream& os, unsigned codepoint) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
+ RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
+ os.Put(codepoint);
+ }
+
+ template<typename OutputStream>
+ static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
+ RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
+ PutUnsafe(os, codepoint);
+ }
+
+ template <typename InputStream>
+ static bool Decode(InputStream& is, unsigned* codepoint) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
+ Ch c = is.Take();
+ *codepoint = c;
+ return c <= 0x10FFFF;
+ }
+
+ template <typename InputStream, typename OutputStream>
+ static bool Validate(InputStream& is, OutputStream& os) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
+ Ch c;
+ os.Put(c = is.Take());
+ return c <= 0x10FFFF;
+ }
+};
+
+//! UTF-32 little endian enocoding.
+template<typename CharType = unsigned>
+struct UTF32LE : UTF32<CharType> {
+ template <typename InputByteStream>
+ static CharType TakeBOM(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ CharType c = Take(is);
+ return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c;
+ }
+
+ template <typename InputByteStream>
+ static CharType Take(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ unsigned c = static_cast<uint8_t>(is.Take());
+ c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
+ c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16;
+ c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24;
+ return static_cast<CharType>(c);
+ }
+
+ template <typename OutputByteStream>
+ static void PutBOM(OutputByteStream& os) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
+ os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
+ os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
+ os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
+ }
+
+ template <typename OutputByteStream>
+ static void Put(OutputByteStream& os, CharType c) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ os.Put(static_cast<typename OutputByteStream::Ch>(c & 0xFFu));
+ os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu));
+ os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu));
+ os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu));
+ }
+};
+
+//! UTF-32 big endian encoding.
+template<typename CharType = unsigned>
+struct UTF32BE : UTF32<CharType> {
+ template <typename InputByteStream>
+ static CharType TakeBOM(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ CharType c = Take(is);
+ return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c;
+ }
+
+ template <typename InputByteStream>
+ static CharType Take(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24;
+ c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16;
+ c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
+ c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take()));
+ return static_cast<CharType>(c);
+ }
+
+ template <typename OutputByteStream>
+ static void PutBOM(OutputByteStream& os) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
+ os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
+ os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
+ os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
+ }
+
+ template <typename OutputByteStream>
+ static void Put(OutputByteStream& os, CharType c) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu));
+ os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu));
+ os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu));
+ os.Put(static_cast<typename OutputByteStream::Ch>(c & 0xFFu));
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// ASCII
+
+//! ASCII encoding.
+/*! http://en.wikipedia.org/wiki/ASCII
+ \tparam CharType Code unit for storing 7-bit ASCII data. Default is char.
+ \note implements Encoding concept
+*/
+template<typename CharType = char>
+struct ASCII {
+ typedef CharType Ch;
+
+ enum { supportUnicode = 0 };
+
+ template<typename OutputStream>
+ static void Encode(OutputStream& os, unsigned codepoint) {
+ RAPIDJSON_ASSERT(codepoint <= 0x7F);
+ os.Put(static_cast<Ch>(codepoint & 0xFF));
+ }
+
+ template<typename OutputStream>
+ static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
+ RAPIDJSON_ASSERT(codepoint <= 0x7F);
+ PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF));
+ }
+
+ template <typename InputStream>
+ static bool Decode(InputStream& is, unsigned* codepoint) {
+ uint8_t c = static_cast<uint8_t>(is.Take());
+ *codepoint = c;
+ return c <= 0X7F;
+ }
+
+ template <typename InputStream, typename OutputStream>
+ static bool Validate(InputStream& is, OutputStream& os) {
+ uint8_t c = static_cast<uint8_t>(is.Take());
+ os.Put(static_cast<typename OutputStream::Ch>(c));
+ return c <= 0x7F;
+ }
+
+ template <typename InputByteStream>
+ static CharType TakeBOM(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ uint8_t c = static_cast<uint8_t>(Take(is));
+ return static_cast<Ch>(c);
+ }
+
+ template <typename InputByteStream>
+ static Ch Take(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ return static_cast<Ch>(is.Take());
+ }
+
+ template <typename OutputByteStream>
+ static void PutBOM(OutputByteStream& os) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ (void)os;
+ }
+
+ template <typename OutputByteStream>
+ static void Put(OutputByteStream& os, Ch c) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ os.Put(static_cast<typename OutputByteStream::Ch>(c));
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// AutoUTF
+
+//! Runtime-specified UTF encoding type of a stream.
+enum UTFType {
+ kUTF8 = 0, //!< UTF-8.
+ kUTF16LE = 1, //!< UTF-16 little endian.
+ kUTF16BE = 2, //!< UTF-16 big endian.
+ kUTF32LE = 3, //!< UTF-32 little endian.
+ kUTF32BE = 4 //!< UTF-32 big endian.
+};
+
+//! Dynamically select encoding according to stream's runtime-specified UTF encoding type.
+/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType().
+*/
+template<typename CharType>
+struct AutoUTF {
+ typedef CharType Ch;
+
+ enum { supportUnicode = 1 };
+
+#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
+
+ template<typename OutputStream>
+ RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) {
+ typedef void (*EncodeFunc)(OutputStream&, unsigned);
+ static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) };
+ (*f[os.GetType()])(os, codepoint);
+ }
+
+ template<typename OutputStream>
+ RAPIDJSON_FORCEINLINE static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
+ typedef void (*EncodeFunc)(OutputStream&, unsigned);
+ static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) };
+ (*f[os.GetType()])(os, codepoint);
+ }
+
+ template <typename InputStream>
+ RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) {
+ typedef bool (*DecodeFunc)(InputStream&, unsigned*);
+ static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) };
+ return (*f[is.GetType()])(is, codepoint);
+ }
+
+ template <typename InputStream, typename OutputStream>
+ RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
+ typedef bool (*ValidateFunc)(InputStream&, OutputStream&);
+ static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) };
+ return (*f[is.GetType()])(is, os);
+ }
+
+#undef RAPIDJSON_ENCODINGS_FUNC
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Transcoder
+
+//! Encoding conversion.
+template<typename SourceEncoding, typename TargetEncoding>
+struct Transcoder {
+ //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream.
+ template<typename InputStream, typename OutputStream>
+ RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) {
+ unsigned codepoint;
+ if (!SourceEncoding::Decode(is, &codepoint))
+ return false;
+ TargetEncoding::Encode(os, codepoint);
+ return true;
+ }
+
+ template<typename InputStream, typename OutputStream>
+ RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) {
+ unsigned codepoint;
+ if (!SourceEncoding::Decode(is, &codepoint))
+ return false;
+ TargetEncoding::EncodeUnsafe(os, codepoint);
+ return true;
+ }
+
+ //! Validate one Unicode codepoint from an encoded stream.
+ template<typename InputStream, typename OutputStream>
+ RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
+ return Transcode(is, os); // Since source/target encoding is different, must transcode.
+ }
+};
+
+// Forward declaration.
+template<typename Stream>
+inline void PutUnsafe(Stream& stream, typename Stream::Ch c);
+
+//! Specialization of Transcoder with same source and target encoding.
+template<typename Encoding>
+struct Transcoder<Encoding, Encoding> {
+ template<typename InputStream, typename OutputStream>
+ RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) {
+ os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class.
+ return true;
+ }
+
+ template<typename InputStream, typename OutputStream>
+ RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) {
+ PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class.
+ return true;
+ }
+
+ template<typename InputStream, typename OutputStream>
+ RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
+ return Encoding::Validate(is, os); // source/target encoding are the same
+ }
+};
+
+RAPIDJSON_NAMESPACE_END
+
+#if defined(__GNUC__) || defined(_MSC_VER)
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_ENCODINGS_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/error/en.h b/ext/librethinkdbxx/src/rapidjson/error/en.h
new file mode 100644
index 00000000..2db838bf
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/error/en.h
@@ -0,0 +1,74 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_ERROR_EN_H_
+#define RAPIDJSON_ERROR_EN_H_
+
+#include "error.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(switch-enum)
+RAPIDJSON_DIAG_OFF(covered-switch-default)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Maps error code of parsing into error message.
+/*!
+ \ingroup RAPIDJSON_ERRORS
+ \param parseErrorCode Error code obtained in parsing.
+ \return the error message.
+ \note User can make a copy of this function for localization.
+ Using switch-case is safer for future modification of error codes.
+*/
+inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) {
+ switch (parseErrorCode) {
+ case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error.");
+
+ case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty.");
+ case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values.");
+
+ case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value.");
+
+ case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member.");
+ case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member.");
+ case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member.");
+
+ case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element.");
+
+ case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string.");
+ case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid.");
+ case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string.");
+ case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string.");
+ case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string.");
+
+ case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double.");
+ case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number.");
+ case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number.");
+
+ case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error.");
+ case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error.");
+
+ default: return RAPIDJSON_ERROR_STRING("Unknown error.");
+ }
+}
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_ERROR_EN_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/error/error.h b/ext/librethinkdbxx/src/rapidjson/error/error.h
new file mode 100644
index 00000000..95cb31a7
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/error/error.h
@@ -0,0 +1,155 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_ERROR_ERROR_H_
+#define RAPIDJSON_ERROR_ERROR_H_
+
+#include "../rapidjson.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+#endif
+
+/*! \file error.h */
+
+/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_ERROR_CHARTYPE
+
+//! Character type of error messages.
+/*! \ingroup RAPIDJSON_ERRORS
+ The default character type is \c char.
+ On Windows, user can define this macro as \c TCHAR for supporting both
+ unicode/non-unicode settings.
+*/
+#ifndef RAPIDJSON_ERROR_CHARTYPE
+#define RAPIDJSON_ERROR_CHARTYPE char
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_ERROR_STRING
+
+//! Macro for converting string literial to \ref RAPIDJSON_ERROR_CHARTYPE[].
+/*! \ingroup RAPIDJSON_ERRORS
+ By default this conversion macro does nothing.
+ On Windows, user can define this macro as \c _T(x) for supporting both
+ unicode/non-unicode settings.
+*/
+#ifndef RAPIDJSON_ERROR_STRING
+#define RAPIDJSON_ERROR_STRING(x) x
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// ParseErrorCode
+
+//! Error code of parsing.
+/*! \ingroup RAPIDJSON_ERRORS
+ \see GenericReader::Parse, GenericReader::GetParseErrorCode
+*/
+enum ParseErrorCode {
+ kParseErrorNone = 0, //!< No error.
+
+ kParseErrorDocumentEmpty, //!< The document is empty.
+ kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values.
+
+ kParseErrorValueInvalid, //!< Invalid value.
+
+ kParseErrorObjectMissName, //!< Missing a name for object member.
+ kParseErrorObjectMissColon, //!< Missing a colon after a name of object member.
+ kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member.
+
+ kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element.
+
+ kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string.
+ kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid.
+ kParseErrorStringEscapeInvalid, //!< Invalid escape character in string.
+ kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string.
+ kParseErrorStringInvalidEncoding, //!< Invalid encoding in string.
+
+ kParseErrorNumberTooBig, //!< Number too big to be stored in double.
+ kParseErrorNumberMissFraction, //!< Miss fraction part in number.
+ kParseErrorNumberMissExponent, //!< Miss exponent in number.
+
+ kParseErrorTermination, //!< Parsing was terminated.
+ kParseErrorUnspecificSyntaxError //!< Unspecific syntax error.
+};
+
+//! Result of parsing (wraps ParseErrorCode)
+/*!
+ \ingroup RAPIDJSON_ERRORS
+ \code
+ Document doc;
+ ParseResult ok = doc.Parse("[42]");
+ if (!ok) {
+ fprintf(stderr, "JSON parse error: %s (%u)",
+ GetParseError_En(ok.Code()), ok.Offset());
+ exit(EXIT_FAILURE);
+ }
+ \endcode
+ \see GenericReader::Parse, GenericDocument::Parse
+*/
+struct ParseResult {
+public:
+ //! Default constructor, no error.
+ ParseResult() : code_(kParseErrorNone), offset_(0) {}
+ //! Constructor to set an error.
+ ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {}
+
+ //! Get the error code.
+ ParseErrorCode Code() const { return code_; }
+ //! Get the error offset, if \ref IsError(), 0 otherwise.
+ size_t Offset() const { return offset_; }
+
+ //! Conversion to \c bool, returns \c true, iff !\ref IsError().
+ operator bool() const { return !IsError(); }
+ //! Whether the result is an error.
+ bool IsError() const { return code_ != kParseErrorNone; }
+
+ bool operator==(const ParseResult& that) const { return code_ == that.code_; }
+ bool operator==(ParseErrorCode code) const { return code_ == code; }
+ friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; }
+
+ //! Reset error code.
+ void Clear() { Set(kParseErrorNone); }
+ //! Update error code and offset.
+ void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; }
+
+private:
+ ParseErrorCode code_;
+ size_t offset_;
+};
+
+//! Function pointer type of GetParseError().
+/*! \ingroup RAPIDJSON_ERRORS
+
+ This is the prototype for \c GetParseError_X(), where \c X is a locale.
+ User can dynamically change locale in runtime, e.g.:
+\code
+ GetParseErrorFunc GetParseError = GetParseError_En; // or whatever
+ const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode());
+\endcode
+*/
+typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode);
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_ERROR_ERROR_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/filereadstream.h b/ext/librethinkdbxx/src/rapidjson/filereadstream.h
new file mode 100644
index 00000000..b56ea13b
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/filereadstream.h
@@ -0,0 +1,99 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_FILEREADSTREAM_H_
+#define RAPIDJSON_FILEREADSTREAM_H_
+
+#include "stream.h"
+#include <cstdio>
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+RAPIDJSON_DIAG_OFF(unreachable-code)
+RAPIDJSON_DIAG_OFF(missing-noreturn)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! File byte stream for input using fread().
+/*!
+ \note implements Stream concept
+*/
+class FileReadStream {
+public:
+ typedef char Ch; //!< Character type (byte).
+
+ //! Constructor.
+ /*!
+ \param fp File pointer opened for read.
+ \param buffer user-supplied buffer.
+ \param bufferSize size of buffer in bytes. Must >=4 bytes.
+ */
+ FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) {
+ RAPIDJSON_ASSERT(fp_ != 0);
+ RAPIDJSON_ASSERT(bufferSize >= 4);
+ Read();
+ }
+
+ Ch Peek() const { return *current_; }
+ Ch Take() { Ch c = *current_; Read(); return c; }
+ size_t Tell() const { return count_ + static_cast<size_t>(current_ - buffer_); }
+
+ // Not implemented
+ void Put(Ch) { RAPIDJSON_ASSERT(false); }
+ void Flush() { RAPIDJSON_ASSERT(false); }
+ Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+ // For encoding detection only.
+ const Ch* Peek4() const {
+ return (current_ + 4 <= bufferLast_) ? current_ : 0;
+ }
+
+private:
+ void Read() {
+ if (current_ < bufferLast_)
+ ++current_;
+ else if (!eof_) {
+ count_ += readCount_;
+ readCount_ = fread(buffer_, 1, bufferSize_, fp_);
+ bufferLast_ = buffer_ + readCount_ - 1;
+ current_ = buffer_;
+
+ if (readCount_ < bufferSize_) {
+ buffer_[readCount_] = '\0';
+ ++bufferLast_;
+ eof_ = true;
+ }
+ }
+ }
+
+ std::FILE* fp_;
+ Ch *buffer_;
+ size_t bufferSize_;
+ Ch *bufferLast_;
+ Ch *current_;
+ size_t readCount_;
+ size_t count_; //!< Number of characters read
+ bool eof_;
+};
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_FILESTREAM_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/filewritestream.h b/ext/librethinkdbxx/src/rapidjson/filewritestream.h
new file mode 100644
index 00000000..6378dd60
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/filewritestream.h
@@ -0,0 +1,104 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_FILEWRITESTREAM_H_
+#define RAPIDJSON_FILEWRITESTREAM_H_
+
+#include "stream.h"
+#include <cstdio>
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(unreachable-code)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Wrapper of C file stream for input using fread().
+/*!
+ \note implements Stream concept
+*/
+class FileWriteStream {
+public:
+ typedef char Ch; //!< Character type. Only support char.
+
+ FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) {
+ RAPIDJSON_ASSERT(fp_ != 0);
+ }
+
+ void Put(char c) {
+ if (current_ >= bufferEnd_)
+ Flush();
+
+ *current_++ = c;
+ }
+
+ void PutN(char c, size_t n) {
+ size_t avail = static_cast<size_t>(bufferEnd_ - current_);
+ while (n > avail) {
+ std::memset(current_, c, avail);
+ current_ += avail;
+ Flush();
+ n -= avail;
+ avail = static_cast<size_t>(bufferEnd_ - current_);
+ }
+
+ if (n > 0) {
+ std::memset(current_, c, n);
+ current_ += n;
+ }
+ }
+
+ void Flush() {
+ if (current_ != buffer_) {
+ size_t result = fwrite(buffer_, 1, static_cast<size_t>(current_ - buffer_), fp_);
+ if (result < static_cast<size_t>(current_ - buffer_)) {
+ // failure deliberately ignored at this time
+ // added to avoid warn_unused_result build errors
+ }
+ current_ = buffer_;
+ }
+ }
+
+ // Not implemented
+ char Peek() const { RAPIDJSON_ASSERT(false); return 0; }
+ char Take() { RAPIDJSON_ASSERT(false); return 0; }
+ size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
+ char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; }
+
+private:
+ // Prohibit copy constructor & assignment operator.
+ FileWriteStream(const FileWriteStream&);
+ FileWriteStream& operator=(const FileWriteStream&);
+
+ std::FILE* fp_;
+ char *buffer_;
+ char *bufferEnd_;
+ char *current_;
+};
+
+//! Implement specialized version of PutN() with memset() for better performance.
+template<>
+inline void PutN(FileWriteStream& stream, char c, size_t n) {
+ stream.PutN(c, n);
+}
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_FILESTREAM_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/fwd.h b/ext/librethinkdbxx/src/rapidjson/fwd.h
new file mode 100644
index 00000000..e8104e84
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/fwd.h
@@ -0,0 +1,151 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_FWD_H_
+#define RAPIDJSON_FWD_H_
+
+#include "rapidjson.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+// encodings.h
+
+template<typename CharType> struct UTF8;
+template<typename CharType> struct UTF16;
+template<typename CharType> struct UTF16BE;
+template<typename CharType> struct UTF16LE;
+template<typename CharType> struct UTF32;
+template<typename CharType> struct UTF32BE;
+template<typename CharType> struct UTF32LE;
+template<typename CharType> struct ASCII;
+template<typename CharType> struct AutoUTF;
+
+template<typename SourceEncoding, typename TargetEncoding>
+struct Transcoder;
+
+// allocators.h
+
+class CrtAllocator;
+
+template <typename BaseAllocator>
+class MemoryPoolAllocator;
+
+// stream.h
+
+template <typename Encoding>
+struct GenericStringStream;
+
+typedef GenericStringStream<UTF8<char> > StringStream;
+
+template <typename Encoding>
+struct GenericInsituStringStream;
+
+typedef GenericInsituStringStream<UTF8<char> > InsituStringStream;
+
+// stringbuffer.h
+
+template <typename Encoding, typename Allocator>
+class GenericStringBuffer;
+
+typedef GenericStringBuffer<UTF8<char>, CrtAllocator> StringBuffer;
+
+// filereadstream.h
+
+class FileReadStream;
+
+// filewritestream.h
+
+class FileWriteStream;
+
+// memorybuffer.h
+
+template <typename Allocator>
+struct GenericMemoryBuffer;
+
+typedef GenericMemoryBuffer<CrtAllocator> MemoryBuffer;
+
+// memorystream.h
+
+struct MemoryStream;
+
+// reader.h
+
+template<typename Encoding, typename Derived>
+struct BaseReaderHandler;
+
+template <typename SourceEncoding, typename TargetEncoding, typename StackAllocator>
+class GenericReader;
+
+typedef GenericReader<UTF8<char>, UTF8<char>, CrtAllocator> Reader;
+
+// writer.h
+
+template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags>
+class Writer;
+
+// prettywriter.h
+
+template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags>
+class PrettyWriter;
+
+// document.h
+
+template <typename Encoding, typename Allocator>
+struct GenericMember;
+
+template <bool Const, typename Encoding, typename Allocator>
+class GenericMemberIterator;
+
+template<typename CharType>
+struct GenericStringRef;
+
+template <typename Encoding, typename Allocator>
+class GenericValue;
+
+typedef GenericValue<UTF8<char>, MemoryPoolAllocator<CrtAllocator> > Value;
+
+template <typename Encoding, typename Allocator, typename StackAllocator>
+class GenericDocument;
+
+typedef GenericDocument<UTF8<char>, MemoryPoolAllocator<CrtAllocator>, CrtAllocator> Document;
+
+// pointer.h
+
+template <typename ValueType, typename Allocator>
+class GenericPointer;
+
+typedef GenericPointer<Value, CrtAllocator> Pointer;
+
+// schema.h
+
+template <typename SchemaDocumentType>
+class IGenericRemoteSchemaDocumentProvider;
+
+template <typename ValueT, typename Allocator>
+class GenericSchemaDocument;
+
+typedef GenericSchemaDocument<Value, CrtAllocator> SchemaDocument;
+typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
+
+template <
+ typename SchemaDocumentType,
+ typename OutputHandler,
+ typename StateAllocator>
+class GenericSchemaValidator;
+
+typedef GenericSchemaValidator<SchemaDocument, BaseReaderHandler<UTF8<char>, void>, CrtAllocator> SchemaValidator;
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_RAPIDJSONFWD_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/biginteger.h b/ext/librethinkdbxx/src/rapidjson/internal/biginteger.h
new file mode 100644
index 00000000..9d3e88c9
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/biginteger.h
@@ -0,0 +1,290 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_BIGINTEGER_H_
+#define RAPIDJSON_BIGINTEGER_H_
+
+#include "../rapidjson.h"
+
+#if defined(_MSC_VER) && defined(_M_AMD64)
+#include <intrin.h> // for _umul128
+#pragma intrinsic(_umul128)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+class BigInteger {
+public:
+ typedef uint64_t Type;
+
+ BigInteger(const BigInteger& rhs) : count_(rhs.count_) {
+ std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type));
+ }
+
+ explicit BigInteger(uint64_t u) : count_(1) {
+ digits_[0] = u;
+ }
+
+ BigInteger(const char* decimals, size_t length) : count_(1) {
+ RAPIDJSON_ASSERT(length > 0);
+ digits_[0] = 0;
+ size_t i = 0;
+ const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19
+ while (length >= kMaxDigitPerIteration) {
+ AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration);
+ length -= kMaxDigitPerIteration;
+ i += kMaxDigitPerIteration;
+ }
+
+ if (length > 0)
+ AppendDecimal64(decimals + i, decimals + i + length);
+ }
+
+ BigInteger& operator=(const BigInteger &rhs)
+ {
+ if (this != &rhs) {
+ count_ = rhs.count_;
+ std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type));
+ }
+ return *this;
+ }
+
+ BigInteger& operator=(uint64_t u) {
+ digits_[0] = u;
+ count_ = 1;
+ return *this;
+ }
+
+ BigInteger& operator+=(uint64_t u) {
+ Type backup = digits_[0];
+ digits_[0] += u;
+ for (size_t i = 0; i < count_ - 1; i++) {
+ if (digits_[i] >= backup)
+ return *this; // no carry
+ backup = digits_[i + 1];
+ digits_[i + 1] += 1;
+ }
+
+ // Last carry
+ if (digits_[count_ - 1] < backup)
+ PushBack(1);
+
+ return *this;
+ }
+
+ BigInteger& operator*=(uint64_t u) {
+ if (u == 0) return *this = 0;
+ if (u == 1) return *this;
+ if (*this == 1) return *this = u;
+
+ uint64_t k = 0;
+ for (size_t i = 0; i < count_; i++) {
+ uint64_t hi;
+ digits_[i] = MulAdd64(digits_[i], u, k, &hi);
+ k = hi;
+ }
+
+ if (k > 0)
+ PushBack(k);
+
+ return *this;
+ }
+
+ BigInteger& operator*=(uint32_t u) {
+ if (u == 0) return *this = 0;
+ if (u == 1) return *this;
+ if (*this == 1) return *this = u;
+
+ uint64_t k = 0;
+ for (size_t i = 0; i < count_; i++) {
+ const uint64_t c = digits_[i] >> 32;
+ const uint64_t d = digits_[i] & 0xFFFFFFFF;
+ const uint64_t uc = u * c;
+ const uint64_t ud = u * d;
+ const uint64_t p0 = ud + k;
+ const uint64_t p1 = uc + (p0 >> 32);
+ digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32);
+ k = p1 >> 32;
+ }
+
+ if (k > 0)
+ PushBack(k);
+
+ return *this;
+ }
+
+ BigInteger& operator<<=(size_t shift) {
+ if (IsZero() || shift == 0) return *this;
+
+ size_t offset = shift / kTypeBit;
+ size_t interShift = shift % kTypeBit;
+ RAPIDJSON_ASSERT(count_ + offset <= kCapacity);
+
+ if (interShift == 0) {
+ std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type));
+ count_ += offset;
+ }
+ else {
+ digits_[count_] = 0;
+ for (size_t i = count_; i > 0; i--)
+ digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift));
+ digits_[offset] = digits_[0] << interShift;
+ count_ += offset;
+ if (digits_[count_])
+ count_++;
+ }
+
+ std::memset(digits_, 0, offset * sizeof(Type));
+
+ return *this;
+ }
+
+ bool operator==(const BigInteger& rhs) const {
+ return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0;
+ }
+
+ bool operator==(const Type rhs) const {
+ return count_ == 1 && digits_[0] == rhs;
+ }
+
+ BigInteger& MultiplyPow5(unsigned exp) {
+ static const uint32_t kPow5[12] = {
+ 5,
+ 5 * 5,
+ 5 * 5 * 5,
+ 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5
+ };
+ if (exp == 0) return *this;
+ for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27
+ for (; exp >= 13; exp -= 13) *this *= static_cast<uint32_t>(1220703125u); // 5^13
+ if (exp > 0) *this *= kPow5[exp - 1];
+ return *this;
+ }
+
+ // Compute absolute difference of this and rhs.
+ // Assume this != rhs
+ bool Difference(const BigInteger& rhs, BigInteger* out) const {
+ int cmp = Compare(rhs);
+ RAPIDJSON_ASSERT(cmp != 0);
+ const BigInteger *a, *b; // Makes a > b
+ bool ret;
+ if (cmp < 0) { a = &rhs; b = this; ret = true; }
+ else { a = this; b = &rhs; ret = false; }
+
+ Type borrow = 0;
+ for (size_t i = 0; i < a->count_; i++) {
+ Type d = a->digits_[i] - borrow;
+ if (i < b->count_)
+ d -= b->digits_[i];
+ borrow = (d > a->digits_[i]) ? 1 : 0;
+ out->digits_[i] = d;
+ if (d != 0)
+ out->count_ = i + 1;
+ }
+
+ return ret;
+ }
+
+ int Compare(const BigInteger& rhs) const {
+ if (count_ != rhs.count_)
+ return count_ < rhs.count_ ? -1 : 1;
+
+ for (size_t i = count_; i-- > 0;)
+ if (digits_[i] != rhs.digits_[i])
+ return digits_[i] < rhs.digits_[i] ? -1 : 1;
+
+ return 0;
+ }
+
+ size_t GetCount() const { return count_; }
+ Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; }
+ bool IsZero() const { return count_ == 1 && digits_[0] == 0; }
+
+private:
+ void AppendDecimal64(const char* begin, const char* end) {
+ uint64_t u = ParseUint64(begin, end);
+ if (IsZero())
+ *this = u;
+ else {
+ unsigned exp = static_cast<unsigned>(end - begin);
+ (MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u
+ }
+ }
+
+ void PushBack(Type digit) {
+ RAPIDJSON_ASSERT(count_ < kCapacity);
+ digits_[count_++] = digit;
+ }
+
+ static uint64_t ParseUint64(const char* begin, const char* end) {
+ uint64_t r = 0;
+ for (const char* p = begin; p != end; ++p) {
+ RAPIDJSON_ASSERT(*p >= '0' && *p <= '9');
+ r = r * 10u + static_cast<unsigned>(*p - '0');
+ }
+ return r;
+ }
+
+ // Assume a * b + k < 2^128
+ static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) {
+#if defined(_MSC_VER) && defined(_M_AMD64)
+ uint64_t low = _umul128(a, b, outHigh) + k;
+ if (low < k)
+ (*outHigh)++;
+ return low;
+#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
+ __extension__ typedef unsigned __int128 uint128;
+ uint128 p = static_cast<uint128>(a) * static_cast<uint128>(b);
+ p += k;
+ *outHigh = static_cast<uint64_t>(p >> 64);
+ return static_cast<uint64_t>(p);
+#else
+ const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32;
+ uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1;
+ x1 += (x0 >> 32); // can't give carry
+ x1 += x2;
+ if (x1 < x2)
+ x3 += (static_cast<uint64_t>(1) << 32);
+ uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF);
+ uint64_t hi = x3 + (x1 >> 32);
+
+ lo += k;
+ if (lo < k)
+ hi++;
+ *outHigh = hi;
+ return lo;
+#endif
+ }
+
+ static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000
+ static const size_t kCapacity = kBitCount / sizeof(Type);
+ static const size_t kTypeBit = sizeof(Type) * 8;
+
+ Type digits_[kCapacity];
+ size_t count_;
+};
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_BIGINTEGER_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/diyfp.h b/ext/librethinkdbxx/src/rapidjson/internal/diyfp.h
new file mode 100644
index 00000000..c9fefdc6
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/diyfp.h
@@ -0,0 +1,258 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
+// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with
+// integers." ACM Sigplan Notices 45.6 (2010): 233-243.
+
+#ifndef RAPIDJSON_DIYFP_H_
+#define RAPIDJSON_DIYFP_H_
+
+#include "../rapidjson.h"
+
+#if defined(_MSC_VER) && defined(_M_AMD64)
+#include <intrin.h>
+#pragma intrinsic(_BitScanReverse64)
+#pragma intrinsic(_umul128)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+#endif
+
+struct DiyFp {
+ DiyFp() : f(), e() {}
+
+ DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {}
+
+ explicit DiyFp(double d) {
+ union {
+ double d;
+ uint64_t u64;
+ } u = { d };
+
+ int biased_e = static_cast<int>((u.u64 & kDpExponentMask) >> kDpSignificandSize);
+ uint64_t significand = (u.u64 & kDpSignificandMask);
+ if (biased_e != 0) {
+ f = significand + kDpHiddenBit;
+ e = biased_e - kDpExponentBias;
+ }
+ else {
+ f = significand;
+ e = kDpMinExponent + 1;
+ }
+ }
+
+ DiyFp operator-(const DiyFp& rhs) const {
+ return DiyFp(f - rhs.f, e);
+ }
+
+ DiyFp operator*(const DiyFp& rhs) const {
+#if defined(_MSC_VER) && defined(_M_AMD64)
+ uint64_t h;
+ uint64_t l = _umul128(f, rhs.f, &h);
+ if (l & (uint64_t(1) << 63)) // rounding
+ h++;
+ return DiyFp(h, e + rhs.e + 64);
+#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
+ __extension__ typedef unsigned __int128 uint128;
+ uint128 p = static_cast<uint128>(f) * static_cast<uint128>(rhs.f);
+ uint64_t h = static_cast<uint64_t>(p >> 64);
+ uint64_t l = static_cast<uint64_t>(p);
+ if (l & (uint64_t(1) << 63)) // rounding
+ h++;
+ return DiyFp(h, e + rhs.e + 64);
+#else
+ const uint64_t M32 = 0xFFFFFFFF;
+ const uint64_t a = f >> 32;
+ const uint64_t b = f & M32;
+ const uint64_t c = rhs.f >> 32;
+ const uint64_t d = rhs.f & M32;
+ const uint64_t ac = a * c;
+ const uint64_t bc = b * c;
+ const uint64_t ad = a * d;
+ const uint64_t bd = b * d;
+ uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32);
+ tmp += 1U << 31; /// mult_round
+ return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64);
+#endif
+ }
+
+ DiyFp Normalize() const {
+#if defined(_MSC_VER) && defined(_M_AMD64)
+ unsigned long index;
+ _BitScanReverse64(&index, f);
+ return DiyFp(f << (63 - index), e - (63 - index));
+#elif defined(__GNUC__) && __GNUC__ >= 4
+ int s = __builtin_clzll(f);
+ return DiyFp(f << s, e - s);
+#else
+ DiyFp res = *this;
+ while (!(res.f & (static_cast<uint64_t>(1) << 63))) {
+ res.f <<= 1;
+ res.e--;
+ }
+ return res;
+#endif
+ }
+
+ DiyFp NormalizeBoundary() const {
+ DiyFp res = *this;
+ while (!(res.f & (kDpHiddenBit << 1))) {
+ res.f <<= 1;
+ res.e--;
+ }
+ res.f <<= (kDiySignificandSize - kDpSignificandSize - 2);
+ res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2);
+ return res;
+ }
+
+ void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const {
+ DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary();
+ DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1);
+ mi.f <<= mi.e - pl.e;
+ mi.e = pl.e;
+ *plus = pl;
+ *minus = mi;
+ }
+
+ double ToDouble() const {
+ union {
+ double d;
+ uint64_t u64;
+ }u;
+ const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 :
+ static_cast<uint64_t>(e + kDpExponentBias);
+ u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize);
+ return u.d;
+ }
+
+ static const int kDiySignificandSize = 64;
+ static const int kDpSignificandSize = 52;
+ static const int kDpExponentBias = 0x3FF + kDpSignificandSize;
+ static const int kDpMaxExponent = 0x7FF - kDpExponentBias;
+ static const int kDpMinExponent = -kDpExponentBias;
+ static const int kDpDenormalExponent = -kDpExponentBias + 1;
+ static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
+ static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
+ static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
+
+ uint64_t f;
+ int e;
+};
+
+inline DiyFp GetCachedPowerByIndex(size_t index) {
+ // 10^-348, 10^-340, ..., 10^340
+ static const uint64_t kCachedPowers_F[] = {
+ RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76),
+ RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea),
+ RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df),
+ RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f),
+ RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c),
+ RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5),
+ RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d),
+ RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637),
+ RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7),
+ RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5),
+ RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b),
+ RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996),
+ RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6),
+ RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8),
+ RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053),
+ RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd),
+ RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94),
+ RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b),
+ RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac),
+ RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3),
+ RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb),
+ RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c),
+ RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000),
+ RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984),
+ RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70),
+ RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245),
+ RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8),
+ RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a),
+ RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea),
+ RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85),
+ RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2),
+ RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3),
+ RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25),
+ RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece),
+ RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5),
+ RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a),
+ RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c),
+ RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a),
+ RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129),
+ RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429),
+ RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d),
+ RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841),
+ RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9),
+ RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b)
+ };
+ static const int16_t kCachedPowers_E[] = {
+ -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980,
+ -954, -927, -901, -874, -847, -821, -794, -768, -741, -715,
+ -688, -661, -635, -608, -582, -555, -529, -502, -475, -449,
+ -422, -396, -369, -343, -316, -289, -263, -236, -210, -183,
+ -157, -130, -103, -77, -50, -24, 3, 30, 56, 83,
+ 109, 136, 162, 189, 216, 242, 269, 295, 322, 348,
+ 375, 402, 428, 455, 481, 508, 534, 561, 588, 614,
+ 641, 667, 694, 720, 747, 774, 800, 827, 853, 880,
+ 907, 933, 960, 986, 1013, 1039, 1066
+ };
+ return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]);
+}
+
+inline DiyFp GetCachedPower(int e, int* K) {
+
+ //int k = static_cast<int>(ceil((-61 - e) * 0.30102999566398114)) + 374;
+ double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive
+ int k = static_cast<int>(dk);
+ if (dk - k > 0.0)
+ k++;
+
+ unsigned index = static_cast<unsigned>((k >> 3) + 1);
+ *K = -(-348 + static_cast<int>(index << 3)); // decimal exponent no need lookup table
+
+ return GetCachedPowerByIndex(index);
+}
+
+inline DiyFp GetCachedPower10(int exp, int *outExp) {
+ unsigned index = (static_cast<unsigned>(exp) + 348u) / 8u;
+ *outExp = -348 + static_cast<int>(index) * 8;
+ return GetCachedPowerByIndex(index);
+ }
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+RAPIDJSON_DIAG_OFF(padded)
+#endif
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_DIYFP_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/dtoa.h b/ext/librethinkdbxx/src/rapidjson/internal/dtoa.h
new file mode 100644
index 00000000..8d6350e6
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/dtoa.h
@@ -0,0 +1,245 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
+// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with
+// integers." ACM Sigplan Notices 45.6 (2010): 233-243.
+
+#ifndef RAPIDJSON_DTOA_
+#define RAPIDJSON_DTOA_
+
+#include "itoa.h" // GetDigitsLut()
+#include "diyfp.h"
+#include "ieee754.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124
+#endif
+
+inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) {
+ while (rest < wp_w && delta - rest >= ten_kappa &&
+ (rest + ten_kappa < wp_w || /// closer
+ wp_w - rest > rest + ten_kappa - wp_w)) {
+ buffer[len - 1]--;
+ rest += ten_kappa;
+ }
+}
+
+inline unsigned CountDecimalDigit32(uint32_t n) {
+ // Simple pure C++ implementation was faster than __builtin_clz version in this situation.
+ if (n < 10) return 1;
+ if (n < 100) return 2;
+ if (n < 1000) return 3;
+ if (n < 10000) return 4;
+ if (n < 100000) return 5;
+ if (n < 1000000) return 6;
+ if (n < 10000000) return 7;
+ if (n < 100000000) return 8;
+ // Will not reach 10 digits in DigitGen()
+ //if (n < 1000000000) return 9;
+ //return 10;
+ return 9;
+}
+
+inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) {
+ static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
+ const DiyFp one(uint64_t(1) << -Mp.e, Mp.e);
+ const DiyFp wp_w = Mp - W;
+ uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);
+ uint64_t p2 = Mp.f & (one.f - 1);
+ unsigned kappa = CountDecimalDigit32(p1); // kappa in [0, 9]
+ *len = 0;
+
+ while (kappa > 0) {
+ uint32_t d = 0;
+ switch (kappa) {
+ case 9: d = p1 / 100000000; p1 %= 100000000; break;
+ case 8: d = p1 / 10000000; p1 %= 10000000; break;
+ case 7: d = p1 / 1000000; p1 %= 1000000; break;
+ case 6: d = p1 / 100000; p1 %= 100000; break;
+ case 5: d = p1 / 10000; p1 %= 10000; break;
+ case 4: d = p1 / 1000; p1 %= 1000; break;
+ case 3: d = p1 / 100; p1 %= 100; break;
+ case 2: d = p1 / 10; p1 %= 10; break;
+ case 1: d = p1; p1 = 0; break;
+ default:;
+ }
+ if (d || *len)
+ buffer[(*len)++] = static_cast<char>('0' + static_cast<char>(d));
+ kappa--;
+ uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
+ if (tmp <= delta) {
+ *K += kappa;
+ GrisuRound(buffer, *len, delta, tmp, static_cast<uint64_t>(kPow10[kappa]) << -one.e, wp_w.f);
+ return;
+ }
+ }
+
+ // kappa = 0
+ for (;;) {
+ p2 *= 10;
+ delta *= 10;
+ char d = static_cast<char>(p2 >> -one.e);
+ if (d || *len)
+ buffer[(*len)++] = static_cast<char>('0' + d);
+ p2 &= one.f - 1;
+ kappa--;
+ if (p2 < delta) {
+ *K += kappa;
+ int index = -static_cast<int>(kappa);
+ GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[-static_cast<int>(kappa)] : 0));
+ return;
+ }
+ }
+}
+
+inline void Grisu2(double value, char* buffer, int* length, int* K) {
+ const DiyFp v(value);
+ DiyFp w_m, w_p;
+ v.NormalizedBoundaries(&w_m, &w_p);
+
+ const DiyFp c_mk = GetCachedPower(w_p.e, K);
+ const DiyFp W = v.Normalize() * c_mk;
+ DiyFp Wp = w_p * c_mk;
+ DiyFp Wm = w_m * c_mk;
+ Wm.f++;
+ Wp.f--;
+ DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K);
+}
+
+inline char* WriteExponent(int K, char* buffer) {
+ if (K < 0) {
+ *buffer++ = '-';
+ K = -K;
+ }
+
+ if (K >= 100) {
+ *buffer++ = static_cast<char>('0' + static_cast<char>(K / 100));
+ K %= 100;
+ const char* d = GetDigitsLut() + K * 2;
+ *buffer++ = d[0];
+ *buffer++ = d[1];
+ }
+ else if (K >= 10) {
+ const char* d = GetDigitsLut() + K * 2;
+ *buffer++ = d[0];
+ *buffer++ = d[1];
+ }
+ else
+ *buffer++ = static_cast<char>('0' + static_cast<char>(K));
+
+ return buffer;
+}
+
+inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) {
+ const int kk = length + k; // 10^(kk-1) <= v < 10^kk
+
+ if (0 <= k && kk <= 21) {
+ // 1234e7 -> 12340000000
+ for (int i = length; i < kk; i++)
+ buffer[i] = '0';
+ buffer[kk] = '.';
+ buffer[kk + 1] = '0';
+ return &buffer[kk + 2];
+ }
+ else if (0 < kk && kk <= 21) {
+ // 1234e-2 -> 12.34
+ std::memmove(&buffer[kk + 1], &buffer[kk], static_cast<size_t>(length - kk));
+ buffer[kk] = '.';
+ if (0 > k + maxDecimalPlaces) {
+ // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1
+ // Remove extra trailing zeros (at least one) after truncation.
+ for (int i = kk + maxDecimalPlaces; i > kk + 1; i--)
+ if (buffer[i] != '0')
+ return &buffer[i + 1];
+ return &buffer[kk + 2]; // Reserve one zero
+ }
+ else
+ return &buffer[length + 1];
+ }
+ else if (-6 < kk && kk <= 0) {
+ // 1234e-6 -> 0.001234
+ const int offset = 2 - kk;
+ std::memmove(&buffer[offset], &buffer[0], static_cast<size_t>(length));
+ buffer[0] = '0';
+ buffer[1] = '.';
+ for (int i = 2; i < offset; i++)
+ buffer[i] = '0';
+ if (length - kk > maxDecimalPlaces) {
+ // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1
+ // Remove extra trailing zeros (at least one) after truncation.
+ for (int i = maxDecimalPlaces + 1; i > 2; i--)
+ if (buffer[i] != '0')
+ return &buffer[i + 1];
+ return &buffer[3]; // Reserve one zero
+ }
+ else
+ return &buffer[length + offset];
+ }
+ else if (kk < -maxDecimalPlaces) {
+ // Truncate to zero
+ buffer[0] = '0';
+ buffer[1] = '.';
+ buffer[2] = '0';
+ return &buffer[3];
+ }
+ else if (length == 1) {
+ // 1e30
+ buffer[1] = 'e';
+ return WriteExponent(kk - 1, &buffer[2]);
+ }
+ else {
+ // 1234e30 -> 1.234e33
+ std::memmove(&buffer[2], &buffer[1], static_cast<size_t>(length - 1));
+ buffer[1] = '.';
+ buffer[length + 1] = 'e';
+ return WriteExponent(kk - 1, &buffer[0 + length + 2]);
+ }
+}
+
+inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) {
+ RAPIDJSON_ASSERT(maxDecimalPlaces >= 1);
+ Double d(value);
+ if (d.IsZero()) {
+ if (d.Sign())
+ *buffer++ = '-'; // -0.0, Issue #289
+ buffer[0] = '0';
+ buffer[1] = '.';
+ buffer[2] = '0';
+ return &buffer[3];
+ }
+ else {
+ if (value < 0) {
+ *buffer++ = '-';
+ value = -value;
+ }
+ int length, K;
+ Grisu2(value, buffer, &length, &K);
+ return Prettify(buffer, length, K, maxDecimalPlaces);
+ }
+}
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_DTOA_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/ieee754.h b/ext/librethinkdbxx/src/rapidjson/internal/ieee754.h
new file mode 100644
index 00000000..82bb0b99
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/ieee754.h
@@ -0,0 +1,78 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_IEEE754_
+#define RAPIDJSON_IEEE754_
+
+#include "../rapidjson.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+class Double {
+public:
+ Double() {}
+ Double(double d) : d_(d) {}
+ Double(uint64_t u) : u_(u) {}
+
+ double Value() const { return d_; }
+ uint64_t Uint64Value() const { return u_; }
+
+ double NextPositiveDouble() const {
+ RAPIDJSON_ASSERT(!Sign());
+ return Double(u_ + 1).Value();
+ }
+
+ bool Sign() const { return (u_ & kSignMask) != 0; }
+ uint64_t Significand() const { return u_ & kSignificandMask; }
+ int Exponent() const { return static_cast<int>(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); }
+
+ bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; }
+ bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; }
+ bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; }
+ bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; }
+ bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; }
+
+ uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); }
+ int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; }
+ uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; }
+
+ static unsigned EffectiveSignificandSize(int order) {
+ if (order >= -1021)
+ return 53;
+ else if (order <= -1074)
+ return 0;
+ else
+ return static_cast<unsigned>(order) + 1074;
+ }
+
+private:
+ static const int kSignificandSize = 52;
+ static const int kExponentBias = 0x3FF;
+ static const int kDenormalExponent = 1 - kExponentBias;
+ static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000);
+ static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
+ static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
+ static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
+
+ union {
+ double d_;
+ uint64_t u_;
+ };
+};
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_IEEE754_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/itoa.h b/ext/librethinkdbxx/src/rapidjson/internal/itoa.h
new file mode 100644
index 00000000..01a4e7e7
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/itoa.h
@@ -0,0 +1,304 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_ITOA_
+#define RAPIDJSON_ITOA_
+
+#include "../rapidjson.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+inline const char* GetDigitsLut() {
+ static const char cDigitsLut[200] = {
+ '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9',
+ '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9',
+ '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9',
+ '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9',
+ '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9',
+ '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9',
+ '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9',
+ '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9',
+ '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9',
+ '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9'
+ };
+ return cDigitsLut;
+}
+
+inline char* u32toa(uint32_t value, char* buffer) {
+ const char* cDigitsLut = GetDigitsLut();
+
+ if (value < 10000) {
+ const uint32_t d1 = (value / 100) << 1;
+ const uint32_t d2 = (value % 100) << 1;
+
+ if (value >= 1000)
+ *buffer++ = cDigitsLut[d1];
+ if (value >= 100)
+ *buffer++ = cDigitsLut[d1 + 1];
+ if (value >= 10)
+ *buffer++ = cDigitsLut[d2];
+ *buffer++ = cDigitsLut[d2 + 1];
+ }
+ else if (value < 100000000) {
+ // value = bbbbcccc
+ const uint32_t b = value / 10000;
+ const uint32_t c = value % 10000;
+
+ const uint32_t d1 = (b / 100) << 1;
+ const uint32_t d2 = (b % 100) << 1;
+
+ const uint32_t d3 = (c / 100) << 1;
+ const uint32_t d4 = (c % 100) << 1;
+
+ if (value >= 10000000)
+ *buffer++ = cDigitsLut[d1];
+ if (value >= 1000000)
+ *buffer++ = cDigitsLut[d1 + 1];
+ if (value >= 100000)
+ *buffer++ = cDigitsLut[d2];
+ *buffer++ = cDigitsLut[d2 + 1];
+
+ *buffer++ = cDigitsLut[d3];
+ *buffer++ = cDigitsLut[d3 + 1];
+ *buffer++ = cDigitsLut[d4];
+ *buffer++ = cDigitsLut[d4 + 1];
+ }
+ else {
+ // value = aabbbbcccc in decimal
+
+ const uint32_t a = value / 100000000; // 1 to 42
+ value %= 100000000;
+
+ if (a >= 10) {
+ const unsigned i = a << 1;
+ *buffer++ = cDigitsLut[i];
+ *buffer++ = cDigitsLut[i + 1];
+ }
+ else
+ *buffer++ = static_cast<char>('0' + static_cast<char>(a));
+
+ const uint32_t b = value / 10000; // 0 to 9999
+ const uint32_t c = value % 10000; // 0 to 9999
+
+ const uint32_t d1 = (b / 100) << 1;
+ const uint32_t d2 = (b % 100) << 1;
+
+ const uint32_t d3 = (c / 100) << 1;
+ const uint32_t d4 = (c % 100) << 1;
+
+ *buffer++ = cDigitsLut[d1];
+ *buffer++ = cDigitsLut[d1 + 1];
+ *buffer++ = cDigitsLut[d2];
+ *buffer++ = cDigitsLut[d2 + 1];
+ *buffer++ = cDigitsLut[d3];
+ *buffer++ = cDigitsLut[d3 + 1];
+ *buffer++ = cDigitsLut[d4];
+ *buffer++ = cDigitsLut[d4 + 1];
+ }
+ return buffer;
+}
+
+inline char* i32toa(int32_t value, char* buffer) {
+ uint32_t u = static_cast<uint32_t>(value);
+ if (value < 0) {
+ *buffer++ = '-';
+ u = ~u + 1;
+ }
+
+ return u32toa(u, buffer);
+}
+
+inline char* u64toa(uint64_t value, char* buffer) {
+ const char* cDigitsLut = GetDigitsLut();
+ const uint64_t kTen8 = 100000000;
+ const uint64_t kTen9 = kTen8 * 10;
+ const uint64_t kTen10 = kTen8 * 100;
+ const uint64_t kTen11 = kTen8 * 1000;
+ const uint64_t kTen12 = kTen8 * 10000;
+ const uint64_t kTen13 = kTen8 * 100000;
+ const uint64_t kTen14 = kTen8 * 1000000;
+ const uint64_t kTen15 = kTen8 * 10000000;
+ const uint64_t kTen16 = kTen8 * kTen8;
+
+ if (value < kTen8) {
+ uint32_t v = static_cast<uint32_t>(value);
+ if (v < 10000) {
+ const uint32_t d1 = (v / 100) << 1;
+ const uint32_t d2 = (v % 100) << 1;
+
+ if (v >= 1000)
+ *buffer++ = cDigitsLut[d1];
+ if (v >= 100)
+ *buffer++ = cDigitsLut[d1 + 1];
+ if (v >= 10)
+ *buffer++ = cDigitsLut[d2];
+ *buffer++ = cDigitsLut[d2 + 1];
+ }
+ else {
+ // value = bbbbcccc
+ const uint32_t b = v / 10000;
+ const uint32_t c = v % 10000;
+
+ const uint32_t d1 = (b / 100) << 1;
+ const uint32_t d2 = (b % 100) << 1;
+
+ const uint32_t d3 = (c / 100) << 1;
+ const uint32_t d4 = (c % 100) << 1;
+
+ if (value >= 10000000)
+ *buffer++ = cDigitsLut[d1];
+ if (value >= 1000000)
+ *buffer++ = cDigitsLut[d1 + 1];
+ if (value >= 100000)
+ *buffer++ = cDigitsLut[d2];
+ *buffer++ = cDigitsLut[d2 + 1];
+
+ *buffer++ = cDigitsLut[d3];
+ *buffer++ = cDigitsLut[d3 + 1];
+ *buffer++ = cDigitsLut[d4];
+ *buffer++ = cDigitsLut[d4 + 1];
+ }
+ }
+ else if (value < kTen16) {
+ const uint32_t v0 = static_cast<uint32_t>(value / kTen8);
+ const uint32_t v1 = static_cast<uint32_t>(value % kTen8);
+
+ const uint32_t b0 = v0 / 10000;
+ const uint32_t c0 = v0 % 10000;
+
+ const uint32_t d1 = (b0 / 100) << 1;
+ const uint32_t d2 = (b0 % 100) << 1;
+
+ const uint32_t d3 = (c0 / 100) << 1;
+ const uint32_t d4 = (c0 % 100) << 1;
+
+ const uint32_t b1 = v1 / 10000;
+ const uint32_t c1 = v1 % 10000;
+
+ const uint32_t d5 = (b1 / 100) << 1;
+ const uint32_t d6 = (b1 % 100) << 1;
+
+ const uint32_t d7 = (c1 / 100) << 1;
+ const uint32_t d8 = (c1 % 100) << 1;
+
+ if (value >= kTen15)
+ *buffer++ = cDigitsLut[d1];
+ if (value >= kTen14)
+ *buffer++ = cDigitsLut[d1 + 1];
+ if (value >= kTen13)
+ *buffer++ = cDigitsLut[d2];
+ if (value >= kTen12)
+ *buffer++ = cDigitsLut[d2 + 1];
+ if (value >= kTen11)
+ *buffer++ = cDigitsLut[d3];
+ if (value >= kTen10)
+ *buffer++ = cDigitsLut[d3 + 1];
+ if (value >= kTen9)
+ *buffer++ = cDigitsLut[d4];
+ if (value >= kTen8)
+ *buffer++ = cDigitsLut[d4 + 1];
+
+ *buffer++ = cDigitsLut[d5];
+ *buffer++ = cDigitsLut[d5 + 1];
+ *buffer++ = cDigitsLut[d6];
+ *buffer++ = cDigitsLut[d6 + 1];
+ *buffer++ = cDigitsLut[d7];
+ *buffer++ = cDigitsLut[d7 + 1];
+ *buffer++ = cDigitsLut[d8];
+ *buffer++ = cDigitsLut[d8 + 1];
+ }
+ else {
+ const uint32_t a = static_cast<uint32_t>(value / kTen16); // 1 to 1844
+ value %= kTen16;
+
+ if (a < 10)
+ *buffer++ = static_cast<char>('0' + static_cast<char>(a));
+ else if (a < 100) {
+ const uint32_t i = a << 1;
+ *buffer++ = cDigitsLut[i];
+ *buffer++ = cDigitsLut[i + 1];
+ }
+ else if (a < 1000) {
+ *buffer++ = static_cast<char>('0' + static_cast<char>(a / 100));
+
+ const uint32_t i = (a % 100) << 1;
+ *buffer++ = cDigitsLut[i];
+ *buffer++ = cDigitsLut[i + 1];
+ }
+ else {
+ const uint32_t i = (a / 100) << 1;
+ const uint32_t j = (a % 100) << 1;
+ *buffer++ = cDigitsLut[i];
+ *buffer++ = cDigitsLut[i + 1];
+ *buffer++ = cDigitsLut[j];
+ *buffer++ = cDigitsLut[j + 1];
+ }
+
+ const uint32_t v0 = static_cast<uint32_t>(value / kTen8);
+ const uint32_t v1 = static_cast<uint32_t>(value % kTen8);
+
+ const uint32_t b0 = v0 / 10000;
+ const uint32_t c0 = v0 % 10000;
+
+ const uint32_t d1 = (b0 / 100) << 1;
+ const uint32_t d2 = (b0 % 100) << 1;
+
+ const uint32_t d3 = (c0 / 100) << 1;
+ const uint32_t d4 = (c0 % 100) << 1;
+
+ const uint32_t b1 = v1 / 10000;
+ const uint32_t c1 = v1 % 10000;
+
+ const uint32_t d5 = (b1 / 100) << 1;
+ const uint32_t d6 = (b1 % 100) << 1;
+
+ const uint32_t d7 = (c1 / 100) << 1;
+ const uint32_t d8 = (c1 % 100) << 1;
+
+ *buffer++ = cDigitsLut[d1];
+ *buffer++ = cDigitsLut[d1 + 1];
+ *buffer++ = cDigitsLut[d2];
+ *buffer++ = cDigitsLut[d2 + 1];
+ *buffer++ = cDigitsLut[d3];
+ *buffer++ = cDigitsLut[d3 + 1];
+ *buffer++ = cDigitsLut[d4];
+ *buffer++ = cDigitsLut[d4 + 1];
+ *buffer++ = cDigitsLut[d5];
+ *buffer++ = cDigitsLut[d5 + 1];
+ *buffer++ = cDigitsLut[d6];
+ *buffer++ = cDigitsLut[d6 + 1];
+ *buffer++ = cDigitsLut[d7];
+ *buffer++ = cDigitsLut[d7 + 1];
+ *buffer++ = cDigitsLut[d8];
+ *buffer++ = cDigitsLut[d8 + 1];
+ }
+
+ return buffer;
+}
+
+inline char* i64toa(int64_t value, char* buffer) {
+ uint64_t u = static_cast<uint64_t>(value);
+ if (value < 0) {
+ *buffer++ = '-';
+ u = ~u + 1;
+ }
+
+ return u64toa(u, buffer);
+}
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_ITOA_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/meta.h b/ext/librethinkdbxx/src/rapidjson/internal/meta.h
new file mode 100644
index 00000000..5a9aaa42
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/meta.h
@@ -0,0 +1,181 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_INTERNAL_META_H_
+#define RAPIDJSON_INTERNAL_META_H_
+
+#include "../rapidjson.h"
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+#if defined(_MSC_VER)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(6334)
+#endif
+
+#if RAPIDJSON_HAS_CXX11_TYPETRAITS
+#include <type_traits>
+#endif
+
+//@cond RAPIDJSON_INTERNAL
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching
+template <typename T> struct Void { typedef void Type; };
+
+///////////////////////////////////////////////////////////////////////////////
+// BoolType, TrueType, FalseType
+//
+template <bool Cond> struct BoolType {
+ static const bool Value = Cond;
+ typedef BoolType Type;
+};
+typedef BoolType<true> TrueType;
+typedef BoolType<false> FalseType;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr
+//
+
+template <bool C> struct SelectIfImpl { template <typename T1, typename T2> struct Apply { typedef T1 Type; }; };
+template <> struct SelectIfImpl<false> { template <typename T1, typename T2> struct Apply { typedef T2 Type; }; };
+template <bool C, typename T1, typename T2> struct SelectIfCond : SelectIfImpl<C>::template Apply<T1,T2> {};
+template <typename C, typename T1, typename T2> struct SelectIf : SelectIfCond<C::Value, T1, T2> {};
+
+template <bool Cond1, bool Cond2> struct AndExprCond : FalseType {};
+template <> struct AndExprCond<true, true> : TrueType {};
+template <bool Cond1, bool Cond2> struct OrExprCond : TrueType {};
+template <> struct OrExprCond<false, false> : FalseType {};
+
+template <typename C> struct BoolExpr : SelectIf<C,TrueType,FalseType>::Type {};
+template <typename C> struct NotExpr : SelectIf<C,FalseType,TrueType>::Type {};
+template <typename C1, typename C2> struct AndExpr : AndExprCond<C1::Value, C2::Value>::Type {};
+template <typename C1, typename C2> struct OrExpr : OrExprCond<C1::Value, C2::Value>::Type {};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// AddConst, MaybeAddConst, RemoveConst
+template <typename T> struct AddConst { typedef const T Type; };
+template <bool Constify, typename T> struct MaybeAddConst : SelectIfCond<Constify, const T, T> {};
+template <typename T> struct RemoveConst { typedef T Type; };
+template <typename T> struct RemoveConst<const T> { typedef T Type; };
+
+
+///////////////////////////////////////////////////////////////////////////////
+// IsSame, IsConst, IsMoreConst, IsPointer
+//
+template <typename T, typename U> struct IsSame : FalseType {};
+template <typename T> struct IsSame<T, T> : TrueType {};
+
+template <typename T> struct IsConst : FalseType {};
+template <typename T> struct IsConst<const T> : TrueType {};
+
+template <typename CT, typename T>
+struct IsMoreConst
+ : AndExpr<IsSame<typename RemoveConst<CT>::Type, typename RemoveConst<T>::Type>,
+ BoolType<IsConst<CT>::Value >= IsConst<T>::Value> >::Type {};
+
+template <typename T> struct IsPointer : FalseType {};
+template <typename T> struct IsPointer<T*> : TrueType {};
+
+///////////////////////////////////////////////////////////////////////////////
+// IsBaseOf
+//
+#if RAPIDJSON_HAS_CXX11_TYPETRAITS
+
+template <typename B, typename D> struct IsBaseOf
+ : BoolType< ::std::is_base_of<B,D>::value> {};
+
+#else // simplified version adopted from Boost
+
+template<typename B, typename D> struct IsBaseOfImpl {
+ RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0);
+ RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0);
+
+ typedef char (&Yes)[1];
+ typedef char (&No) [2];
+
+ template <typename T>
+ static Yes Check(const D*, T);
+ static No Check(const B*, int);
+
+ struct Host {
+ operator const B*() const;
+ operator const D*();
+ };
+
+ enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) };
+};
+
+template <typename B, typename D> struct IsBaseOf
+ : OrExpr<IsSame<B, D>, BoolExpr<IsBaseOfImpl<B, D> > >::Type {};
+
+#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS
+
+
+//////////////////////////////////////////////////////////////////////////
+// EnableIf / DisableIf
+//
+template <bool Condition, typename T = void> struct EnableIfCond { typedef T Type; };
+template <typename T> struct EnableIfCond<false, T> { /* empty */ };
+
+template <bool Condition, typename T = void> struct DisableIfCond { typedef T Type; };
+template <typename T> struct DisableIfCond<true, T> { /* empty */ };
+
+template <typename Condition, typename T = void>
+struct EnableIf : EnableIfCond<Condition::Value, T> {};
+
+template <typename Condition, typename T = void>
+struct DisableIf : DisableIfCond<Condition::Value, T> {};
+
+// SFINAE helpers
+struct SfinaeTag {};
+template <typename T> struct RemoveSfinaeTag;
+template <typename T> struct RemoveSfinaeTag<SfinaeTag&(*)(T)> { typedef T Type; };
+
+#define RAPIDJSON_REMOVEFPTR_(type) \
+ typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \
+ < ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type
+
+#define RAPIDJSON_ENABLEIF(cond) \
+ typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \
+ <RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
+
+#define RAPIDJSON_DISABLEIF(cond) \
+ typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
+ <RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
+
+#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \
+ typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \
+ <RAPIDJSON_REMOVEFPTR_(cond), \
+ RAPIDJSON_REMOVEFPTR_(returntype)>::Type
+
+#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \
+ typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
+ <RAPIDJSON_REMOVEFPTR_(cond), \
+ RAPIDJSON_REMOVEFPTR_(returntype)>::Type
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+//@endcond
+
+#if defined(__GNUC__) || defined(_MSC_VER)
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_INTERNAL_META_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/pow10.h b/ext/librethinkdbxx/src/rapidjson/internal/pow10.h
new file mode 100644
index 00000000..02f475d7
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/pow10.h
@@ -0,0 +1,55 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_POW10_
+#define RAPIDJSON_POW10_
+
+#include "../rapidjson.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+//! Computes integer powers of 10 in double (10.0^n).
+/*! This function uses lookup table for fast and accurate results.
+ \param n non-negative exponent. Must <= 308.
+ \return 10.0^n
+*/
+inline double Pow10(int n) {
+ static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes
+ 1e+0,
+ 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20,
+ 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40,
+ 1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60,
+ 1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80,
+ 1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100,
+ 1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120,
+ 1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140,
+ 1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160,
+ 1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180,
+ 1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200,
+ 1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220,
+ 1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240,
+ 1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260,
+ 1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280,
+ 1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300,
+ 1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308
+ };
+ RAPIDJSON_ASSERT(n >= 0 && n <= 308);
+ return e[n];
+}
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_POW10_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/regex.h b/ext/librethinkdbxx/src/rapidjson/internal/regex.h
new file mode 100644
index 00000000..422a5240
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/regex.h
@@ -0,0 +1,701 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_INTERNAL_REGEX_H_
+#define RAPIDJSON_INTERNAL_REGEX_H_
+
+#include "../allocators.h"
+#include "../stream.h"
+#include "stack.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+RAPIDJSON_DIAG_OFF(switch-enum)
+RAPIDJSON_DIAG_OFF(implicit-fallthrough)
+#endif
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
+#endif
+
+#ifndef RAPIDJSON_REGEX_VERBOSE
+#define RAPIDJSON_REGEX_VERBOSE 0
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericRegex
+
+static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1
+static const SizeType kRegexInvalidRange = ~SizeType(0);
+
+//! Regular expression engine with subset of ECMAscript grammar.
+/*!
+ Supported regular expression syntax:
+ - \c ab Concatenation
+ - \c a|b Alternation
+ - \c a? Zero or one
+ - \c a* Zero or more
+ - \c a+ One or more
+ - \c a{3} Exactly 3 times
+ - \c a{3,} At least 3 times
+ - \c a{3,5} 3 to 5 times
+ - \c (ab) Grouping
+ - \c ^a At the beginning
+ - \c a$ At the end
+ - \c . Any character
+ - \c [abc] Character classes
+ - \c [a-c] Character class range
+ - \c [a-z0-9_] Character class combination
+ - \c [^abc] Negated character classes
+ - \c [^a-c] Negated character class range
+ - \c [\b] Backspace (U+0008)
+ - \c \\| \\\\ ... Escape characters
+ - \c \\f Form feed (U+000C)
+ - \c \\n Line feed (U+000A)
+ - \c \\r Carriage return (U+000D)
+ - \c \\t Tab (U+0009)
+ - \c \\v Vertical tab (U+000B)
+
+ \note This is a Thompson NFA engine, implemented with reference to
+ Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).",
+ https://swtch.com/~rsc/regexp/regexp1.html
+*/
+template <typename Encoding, typename Allocator = CrtAllocator>
+class GenericRegex {
+public:
+ typedef typename Encoding::Ch Ch;
+
+ GenericRegex(const Ch* source, Allocator* allocator = 0) :
+ states_(allocator, 256), ranges_(allocator, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(),
+ stateSet_(), state0_(allocator, 0), state1_(allocator, 0), anchorBegin_(), anchorEnd_()
+ {
+ GenericStringStream<Encoding> ss(source);
+ DecodedStream<GenericStringStream<Encoding> > ds(ss);
+ Parse(ds);
+ }
+
+ ~GenericRegex() {
+ Allocator::Free(stateSet_);
+ }
+
+ bool IsValid() const {
+ return root_ != kRegexInvalidState;
+ }
+
+ template <typename InputStream>
+ bool Match(InputStream& is) const {
+ return SearchWithAnchoring(is, true, true);
+ }
+
+ bool Match(const Ch* s) const {
+ GenericStringStream<Encoding> is(s);
+ return Match(is);
+ }
+
+ template <typename InputStream>
+ bool Search(InputStream& is) const {
+ return SearchWithAnchoring(is, anchorBegin_, anchorEnd_);
+ }
+
+ bool Search(const Ch* s) const {
+ GenericStringStream<Encoding> is(s);
+ return Search(is);
+ }
+
+private:
+ enum Operator {
+ kZeroOrOne,
+ kZeroOrMore,
+ kOneOrMore,
+ kConcatenation,
+ kAlternation,
+ kLeftParenthesis
+ };
+
+ static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.'
+ static const unsigned kRangeCharacterClass = 0xFFFFFFFE;
+ static const unsigned kRangeNegationFlag = 0x80000000;
+
+ struct Range {
+ unsigned start; //
+ unsigned end;
+ SizeType next;
+ };
+
+ struct State {
+ SizeType out; //!< Equals to kInvalid for matching state
+ SizeType out1; //!< Equals to non-kInvalid for split
+ SizeType rangeStart;
+ unsigned codepoint;
+ };
+
+ struct Frag {
+ Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {}
+ SizeType start;
+ SizeType out; //!< link-list of all output states
+ SizeType minIndex;
+ };
+
+ template <typename SourceStream>
+ class DecodedStream {
+ public:
+ DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); }
+ unsigned Peek() { return codepoint_; }
+ unsigned Take() {
+ unsigned c = codepoint_;
+ if (c) // No further decoding when '\0'
+ Decode();
+ return c;
+ }
+
+ private:
+ void Decode() {
+ if (!Encoding::Decode(ss_, &codepoint_))
+ codepoint_ = 0;
+ }
+
+ SourceStream& ss_;
+ unsigned codepoint_;
+ };
+
+ State& GetState(SizeType index) {
+ RAPIDJSON_ASSERT(index < stateCount_);
+ return states_.template Bottom<State>()[index];
+ }
+
+ const State& GetState(SizeType index) const {
+ RAPIDJSON_ASSERT(index < stateCount_);
+ return states_.template Bottom<State>()[index];
+ }
+
+ Range& GetRange(SizeType index) {
+ RAPIDJSON_ASSERT(index < rangeCount_);
+ return ranges_.template Bottom<Range>()[index];
+ }
+
+ const Range& GetRange(SizeType index) const {
+ RAPIDJSON_ASSERT(index < rangeCount_);
+ return ranges_.template Bottom<Range>()[index];
+ }
+
+ template <typename InputStream>
+ void Parse(DecodedStream<InputStream>& ds) {
+ Allocator allocator;
+ Stack<Allocator> operandStack(&allocator, 256); // Frag
+ Stack<Allocator> operatorStack(&allocator, 256); // Operator
+ Stack<Allocator> atomCountStack(&allocator, 256); // unsigned (Atom per parenthesis)
+
+ *atomCountStack.template Push<unsigned>() = 0;
+
+ unsigned codepoint;
+ while (ds.Peek() != 0) {
+ switch (codepoint = ds.Take()) {
+ case '^':
+ anchorBegin_ = true;
+ break;
+
+ case '$':
+ anchorEnd_ = true;
+ break;
+
+ case '|':
+ while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() < kAlternation)
+ if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
+ return;
+ *operatorStack.template Push<Operator>() = kAlternation;
+ *atomCountStack.template Top<unsigned>() = 0;
+ break;
+
+ case '(':
+ *operatorStack.template Push<Operator>() = kLeftParenthesis;
+ *atomCountStack.template Push<unsigned>() = 0;
+ break;
+
+ case ')':
+ while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() != kLeftParenthesis)
+ if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
+ return;
+ if (operatorStack.Empty())
+ return;
+ operatorStack.template Pop<Operator>(1);
+ atomCountStack.template Pop<unsigned>(1);
+ ImplicitConcatenation(atomCountStack, operatorStack);
+ break;
+
+ case '?':
+ if (!Eval(operandStack, kZeroOrOne))
+ return;
+ break;
+
+ case '*':
+ if (!Eval(operandStack, kZeroOrMore))
+ return;
+ break;
+
+ case '+':
+ if (!Eval(operandStack, kOneOrMore))
+ return;
+ break;
+
+ case '{':
+ {
+ unsigned n, m;
+ if (!ParseUnsigned(ds, &n))
+ return;
+
+ if (ds.Peek() == ',') {
+ ds.Take();
+ if (ds.Peek() == '}')
+ m = kInfinityQuantifier;
+ else if (!ParseUnsigned(ds, &m) || m < n)
+ return;
+ }
+ else
+ m = n;
+
+ if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}')
+ return;
+ ds.Take();
+ }
+ break;
+
+ case '.':
+ PushOperand(operandStack, kAnyCharacterClass);
+ ImplicitConcatenation(atomCountStack, operatorStack);
+ break;
+
+ case '[':
+ {
+ SizeType range;
+ if (!ParseRange(ds, &range))
+ return;
+ SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass);
+ GetState(s).rangeStart = range;
+ *operandStack.template Push<Frag>() = Frag(s, s, s);
+ }
+ ImplicitConcatenation(atomCountStack, operatorStack);
+ break;
+
+ case '\\': // Escape character
+ if (!CharacterEscape(ds, &codepoint))
+ return; // Unsupported escape character
+ // fall through to default
+
+ default: // Pattern character
+ PushOperand(operandStack, codepoint);
+ ImplicitConcatenation(atomCountStack, operatorStack);
+ }
+ }
+
+ while (!operatorStack.Empty())
+ if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
+ return;
+
+ // Link the operand to matching state.
+ if (operandStack.GetSize() == sizeof(Frag)) {
+ Frag* e = operandStack.template Pop<Frag>(1);
+ Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0));
+ root_ = e->start;
+
+#if RAPIDJSON_REGEX_VERBOSE
+ printf("root: %d\n", root_);
+ for (SizeType i = 0; i < stateCount_ ; i++) {
+ State& s = GetState(i);
+ printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint);
+ }
+ printf("\n");
+#endif
+ }
+
+ // Preallocate buffer for SearchWithAnchoring()
+ RAPIDJSON_ASSERT(stateSet_ == 0);
+ if (stateCount_ > 0) {
+ stateSet_ = static_cast<unsigned*>(states_.GetAllocator().Malloc(GetStateSetSize()));
+ state0_.template Reserve<SizeType>(stateCount_);
+ state1_.template Reserve<SizeType>(stateCount_);
+ }
+ }
+
+ SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) {
+ State* s = states_.template Push<State>();
+ s->out = out;
+ s->out1 = out1;
+ s->codepoint = codepoint;
+ s->rangeStart = kRegexInvalidRange;
+ return stateCount_++;
+ }
+
+ void PushOperand(Stack<Allocator>& operandStack, unsigned codepoint) {
+ SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint);
+ *operandStack.template Push<Frag>() = Frag(s, s, s);
+ }
+
+ void ImplicitConcatenation(Stack<Allocator>& atomCountStack, Stack<Allocator>& operatorStack) {
+ if (*atomCountStack.template Top<unsigned>())
+ *operatorStack.template Push<Operator>() = kConcatenation;
+ (*atomCountStack.template Top<unsigned>())++;
+ }
+
+ SizeType Append(SizeType l1, SizeType l2) {
+ SizeType old = l1;
+ while (GetState(l1).out != kRegexInvalidState)
+ l1 = GetState(l1).out;
+ GetState(l1).out = l2;
+ return old;
+ }
+
+ void Patch(SizeType l, SizeType s) {
+ for (SizeType next; l != kRegexInvalidState; l = next) {
+ next = GetState(l).out;
+ GetState(l).out = s;
+ }
+ }
+
+ bool Eval(Stack<Allocator>& operandStack, Operator op) {
+ switch (op) {
+ case kConcatenation:
+ RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2);
+ {
+ Frag e2 = *operandStack.template Pop<Frag>(1);
+ Frag e1 = *operandStack.template Pop<Frag>(1);
+ Patch(e1.out, e2.start);
+ *operandStack.template Push<Frag>() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex));
+ }
+ return true;
+
+ case kAlternation:
+ if (operandStack.GetSize() >= sizeof(Frag) * 2) {
+ Frag e2 = *operandStack.template Pop<Frag>(1);
+ Frag e1 = *operandStack.template Pop<Frag>(1);
+ SizeType s = NewState(e1.start, e2.start, 0);
+ *operandStack.template Push<Frag>() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex));
+ return true;
+ }
+ return false;
+
+ case kZeroOrOne:
+ if (operandStack.GetSize() >= sizeof(Frag)) {
+ Frag e = *operandStack.template Pop<Frag>(1);
+ SizeType s = NewState(kRegexInvalidState, e.start, 0);
+ *operandStack.template Push<Frag>() = Frag(s, Append(e.out, s), e.minIndex);
+ return true;
+ }
+ return false;
+
+ case kZeroOrMore:
+ if (operandStack.GetSize() >= sizeof(Frag)) {
+ Frag e = *operandStack.template Pop<Frag>(1);
+ SizeType s = NewState(kRegexInvalidState, e.start, 0);
+ Patch(e.out, s);
+ *operandStack.template Push<Frag>() = Frag(s, s, e.minIndex);
+ return true;
+ }
+ return false;
+
+ default:
+ RAPIDJSON_ASSERT(op == kOneOrMore);
+ if (operandStack.GetSize() >= sizeof(Frag)) {
+ Frag e = *operandStack.template Pop<Frag>(1);
+ SizeType s = NewState(kRegexInvalidState, e.start, 0);
+ Patch(e.out, s);
+ *operandStack.template Push<Frag>() = Frag(e.start, s, e.minIndex);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ bool EvalQuantifier(Stack<Allocator>& operandStack, unsigned n, unsigned m) {
+ RAPIDJSON_ASSERT(n <= m);
+ RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag));
+
+ if (n == 0) {
+ if (m == 0) // a{0} not support
+ return false;
+ else if (m == kInfinityQuantifier)
+ Eval(operandStack, kZeroOrMore); // a{0,} -> a*
+ else {
+ Eval(operandStack, kZeroOrOne); // a{0,5} -> a?
+ for (unsigned i = 0; i < m - 1; i++)
+ CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a?
+ for (unsigned i = 0; i < m - 1; i++)
+ Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a?
+ }
+ return true;
+ }
+
+ for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a
+ CloneTopOperand(operandStack);
+
+ if (m == kInfinityQuantifier)
+ Eval(operandStack, kOneOrMore); // a{3,} -> a a a+
+ else if (m > n) {
+ CloneTopOperand(operandStack); // a{3,5} -> a a a a
+ Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a?
+ for (unsigned i = n; i < m - 1; i++)
+ CloneTopOperand(operandStack); // a{3,5} -> a a a a? a?
+ for (unsigned i = n; i < m; i++)
+ Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a?
+ }
+
+ for (unsigned i = 0; i < n - 1; i++)
+ Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a?
+
+ return true;
+ }
+
+ static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; }
+
+ void CloneTopOperand(Stack<Allocator>& operandStack) {
+ const Frag src = *operandStack.template Top<Frag>(); // Copy constructor to prevent invalidation
+ SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_)
+ State* s = states_.template Push<State>(count);
+ memcpy(s, &GetState(src.minIndex), count * sizeof(State));
+ for (SizeType j = 0; j < count; j++) {
+ if (s[j].out != kRegexInvalidState)
+ s[j].out += count;
+ if (s[j].out1 != kRegexInvalidState)
+ s[j].out1 += count;
+ }
+ *operandStack.template Push<Frag>() = Frag(src.start + count, src.out + count, src.minIndex + count);
+ stateCount_ += count;
+ }
+
+ template <typename InputStream>
+ bool ParseUnsigned(DecodedStream<InputStream>& ds, unsigned* u) {
+ unsigned r = 0;
+ if (ds.Peek() < '0' || ds.Peek() > '9')
+ return false;
+ while (ds.Peek() >= '0' && ds.Peek() <= '9') {
+ if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295
+ return false; // overflow
+ r = r * 10 + (ds.Take() - '0');
+ }
+ *u = r;
+ return true;
+ }
+
+ template <typename InputStream>
+ bool ParseRange(DecodedStream<InputStream>& ds, SizeType* range) {
+ bool isBegin = true;
+ bool negate = false;
+ int step = 0;
+ SizeType start = kRegexInvalidRange;
+ SizeType current = kRegexInvalidRange;
+ unsigned codepoint;
+ while ((codepoint = ds.Take()) != 0) {
+ if (isBegin) {
+ isBegin = false;
+ if (codepoint == '^') {
+ negate = true;
+ continue;
+ }
+ }
+
+ switch (codepoint) {
+ case ']':
+ if (start == kRegexInvalidRange)
+ return false; // Error: nothing inside []
+ if (step == 2) { // Add trailing '-'
+ SizeType r = NewRange('-');
+ RAPIDJSON_ASSERT(current != kRegexInvalidRange);
+ GetRange(current).next = r;
+ }
+ if (negate)
+ GetRange(start).start |= kRangeNegationFlag;
+ *range = start;
+ return true;
+
+ case '\\':
+ if (ds.Peek() == 'b') {
+ ds.Take();
+ codepoint = 0x0008; // Escape backspace character
+ }
+ else if (!CharacterEscape(ds, &codepoint))
+ return false;
+ // fall through to default
+
+ default:
+ switch (step) {
+ case 1:
+ if (codepoint == '-') {
+ step++;
+ break;
+ }
+ // fall through to step 0 for other characters
+
+ case 0:
+ {
+ SizeType r = NewRange(codepoint);
+ if (current != kRegexInvalidRange)
+ GetRange(current).next = r;
+ if (start == kRegexInvalidRange)
+ start = r;
+ current = r;
+ }
+ step = 1;
+ break;
+
+ default:
+ RAPIDJSON_ASSERT(step == 2);
+ GetRange(current).end = codepoint;
+ step = 0;
+ }
+ }
+ }
+ return false;
+ }
+
+ SizeType NewRange(unsigned codepoint) {
+ Range* r = ranges_.template Push<Range>();
+ r->start = r->end = codepoint;
+ r->next = kRegexInvalidRange;
+ return rangeCount_++;
+ }
+
+ template <typename InputStream>
+ bool CharacterEscape(DecodedStream<InputStream>& ds, unsigned* escapedCodepoint) {
+ unsigned codepoint;
+ switch (codepoint = ds.Take()) {
+ case '^':
+ case '$':
+ case '|':
+ case '(':
+ case ')':
+ case '?':
+ case '*':
+ case '+':
+ case '.':
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ case '\\':
+ *escapedCodepoint = codepoint; return true;
+ case 'f': *escapedCodepoint = 0x000C; return true;
+ case 'n': *escapedCodepoint = 0x000A; return true;
+ case 'r': *escapedCodepoint = 0x000D; return true;
+ case 't': *escapedCodepoint = 0x0009; return true;
+ case 'v': *escapedCodepoint = 0x000B; return true;
+ default:
+ return false; // Unsupported escape character
+ }
+ }
+
+ template <typename InputStream>
+ bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) const {
+ RAPIDJSON_ASSERT(IsValid());
+ DecodedStream<InputStream> ds(is);
+
+ state0_.Clear();
+ Stack<Allocator> *current = &state0_, *next = &state1_;
+ const size_t stateSetSize = GetStateSetSize();
+ std::memset(stateSet_, 0, stateSetSize);
+
+ bool matched = AddState(*current, root_);
+ unsigned codepoint;
+ while (!current->Empty() && (codepoint = ds.Take()) != 0) {
+ std::memset(stateSet_, 0, stateSetSize);
+ next->Clear();
+ matched = false;
+ for (const SizeType* s = current->template Bottom<SizeType>(); s != current->template End<SizeType>(); ++s) {
+ const State& sr = GetState(*s);
+ if (sr.codepoint == codepoint ||
+ sr.codepoint == kAnyCharacterClass ||
+ (sr.codepoint == kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint)))
+ {
+ matched = AddState(*next, sr.out) || matched;
+ if (!anchorEnd && matched)
+ return true;
+ }
+ if (!anchorBegin)
+ AddState(*next, root_);
+ }
+ internal::Swap(current, next);
+ }
+
+ return matched;
+ }
+
+ size_t GetStateSetSize() const {
+ return (stateCount_ + 31) / 32 * 4;
+ }
+
+ // Return whether the added states is a match state
+ bool AddState(Stack<Allocator>& l, SizeType index) const {
+ RAPIDJSON_ASSERT(index != kRegexInvalidState);
+
+ const State& s = GetState(index);
+ if (s.out1 != kRegexInvalidState) { // Split
+ bool matched = AddState(l, s.out);
+ return AddState(l, s.out1) || matched;
+ }
+ else if (!(stateSet_[index >> 5] & (1 << (index & 31)))) {
+ stateSet_[index >> 5] |= (1 << (index & 31));
+ *l.template PushUnsafe<SizeType>() = index;
+ }
+ return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation.
+ }
+
+ bool MatchRange(SizeType rangeIndex, unsigned codepoint) const {
+ bool yes = (GetRange(rangeIndex).start & kRangeNegationFlag) == 0;
+ while (rangeIndex != kRegexInvalidRange) {
+ const Range& r = GetRange(rangeIndex);
+ if (codepoint >= (r.start & ~kRangeNegationFlag) && codepoint <= r.end)
+ return yes;
+ rangeIndex = r.next;
+ }
+ return !yes;
+ }
+
+ Stack<Allocator> states_;
+ Stack<Allocator> ranges_;
+ SizeType root_;
+ SizeType stateCount_;
+ SizeType rangeCount_;
+
+ static const unsigned kInfinityQuantifier = ~0u;
+
+ // For SearchWithAnchoring()
+ uint32_t* stateSet_; // allocated by states_.GetAllocator()
+ mutable Stack<Allocator> state0_;
+ mutable Stack<Allocator> state1_;
+ bool anchorBegin_;
+ bool anchorEnd_;
+};
+
+typedef GenericRegex<UTF8<> > Regex;
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_INTERNAL_REGEX_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/stack.h b/ext/librethinkdbxx/src/rapidjson/internal/stack.h
new file mode 100644
index 00000000..022c9aab
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/stack.h
@@ -0,0 +1,230 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_INTERNAL_STACK_H_
+#define RAPIDJSON_INTERNAL_STACK_H_
+
+#include "../allocators.h"
+#include "swap.h"
+
+#if defined(__clang__)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+///////////////////////////////////////////////////////////////////////////////
+// Stack
+
+//! A type-unsafe stack for storing different types of data.
+/*! \tparam Allocator Allocator for allocating stack memory.
+*/
+template <typename Allocator>
+class Stack {
+public:
+ // Optimization note: Do not allocate memory for stack_ in constructor.
+ // Do it lazily when first Push() -> Expand() -> Resize().
+ Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) {
+ }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ Stack(Stack&& rhs)
+ : allocator_(rhs.allocator_),
+ ownAllocator_(rhs.ownAllocator_),
+ stack_(rhs.stack_),
+ stackTop_(rhs.stackTop_),
+ stackEnd_(rhs.stackEnd_),
+ initialCapacity_(rhs.initialCapacity_)
+ {
+ rhs.allocator_ = 0;
+ rhs.ownAllocator_ = 0;
+ rhs.stack_ = 0;
+ rhs.stackTop_ = 0;
+ rhs.stackEnd_ = 0;
+ rhs.initialCapacity_ = 0;
+ }
+#endif
+
+ ~Stack() {
+ Destroy();
+ }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ Stack& operator=(Stack&& rhs) {
+ if (&rhs != this)
+ {
+ Destroy();
+
+ allocator_ = rhs.allocator_;
+ ownAllocator_ = rhs.ownAllocator_;
+ stack_ = rhs.stack_;
+ stackTop_ = rhs.stackTop_;
+ stackEnd_ = rhs.stackEnd_;
+ initialCapacity_ = rhs.initialCapacity_;
+
+ rhs.allocator_ = 0;
+ rhs.ownAllocator_ = 0;
+ rhs.stack_ = 0;
+ rhs.stackTop_ = 0;
+ rhs.stackEnd_ = 0;
+ rhs.initialCapacity_ = 0;
+ }
+ return *this;
+ }
+#endif
+
+ void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT {
+ internal::Swap(allocator_, rhs.allocator_);
+ internal::Swap(ownAllocator_, rhs.ownAllocator_);
+ internal::Swap(stack_, rhs.stack_);
+ internal::Swap(stackTop_, rhs.stackTop_);
+ internal::Swap(stackEnd_, rhs.stackEnd_);
+ internal::Swap(initialCapacity_, rhs.initialCapacity_);
+ }
+
+ void Clear() { stackTop_ = stack_; }
+
+ void ShrinkToFit() {
+ if (Empty()) {
+ // If the stack is empty, completely deallocate the memory.
+ Allocator::Free(stack_);
+ stack_ = 0;
+ stackTop_ = 0;
+ stackEnd_ = 0;
+ }
+ else
+ Resize(GetSize());
+ }
+
+ // Optimization note: try to minimize the size of this function for force inline.
+ // Expansion is run very infrequently, so it is moved to another (probably non-inline) function.
+ template<typename T>
+ RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) {
+ // Expand the stack if needed
+ if (RAPIDJSON_UNLIKELY(stackTop_ + sizeof(T) * count > stackEnd_))
+ Expand<T>(count);
+ }
+
+ template<typename T>
+ RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) {
+ Reserve<T>(count);
+ return PushUnsafe<T>(count);
+ }
+
+ template<typename T>
+ RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) {
+ RAPIDJSON_ASSERT(stackTop_ + sizeof(T) * count <= stackEnd_);
+ T* ret = reinterpret_cast<T*>(stackTop_);
+ stackTop_ += sizeof(T) * count;
+ return ret;
+ }
+
+ template<typename T>
+ T* Pop(size_t count) {
+ RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T));
+ stackTop_ -= count * sizeof(T);
+ return reinterpret_cast<T*>(stackTop_);
+ }
+
+ template<typename T>
+ T* Top() {
+ RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
+ return reinterpret_cast<T*>(stackTop_ - sizeof(T));
+ }
+
+ template<typename T>
+ const T* Top() const {
+ RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
+ return reinterpret_cast<T*>(stackTop_ - sizeof(T));
+ }
+
+ template<typename T>
+ T* End() { return reinterpret_cast<T*>(stackTop_); }
+
+ template<typename T>
+ const T* End() const { return reinterpret_cast<T*>(stackTop_); }
+
+ template<typename T>
+ T* Bottom() { return reinterpret_cast<T*>(stack_); }
+
+ template<typename T>
+ const T* Bottom() const { return reinterpret_cast<T*>(stack_); }
+
+ bool HasAllocator() const {
+ return allocator_ != 0;
+ }
+
+ Allocator& GetAllocator() {
+ RAPIDJSON_ASSERT(allocator_);
+ return *allocator_;
+ }
+
+ bool Empty() const { return stackTop_ == stack_; }
+ size_t GetSize() const { return static_cast<size_t>(stackTop_ - stack_); }
+ size_t GetCapacity() const { return static_cast<size_t>(stackEnd_ - stack_); }
+
+private:
+ template<typename T>
+ void Expand(size_t count) {
+ // Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity.
+ size_t newCapacity;
+ if (stack_ == 0) {
+ if (!allocator_)
+ ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
+ newCapacity = initialCapacity_;
+ } else {
+ newCapacity = GetCapacity();
+ newCapacity += (newCapacity + 1) / 2;
+ }
+ size_t newSize = GetSize() + sizeof(T) * count;
+ if (newCapacity < newSize)
+ newCapacity = newSize;
+
+ Resize(newCapacity);
+ }
+
+ void Resize(size_t newCapacity) {
+ const size_t size = GetSize(); // Backup the current size
+ stack_ = static_cast<char*>(allocator_->Realloc(stack_, GetCapacity(), newCapacity));
+ stackTop_ = stack_ + size;
+ stackEnd_ = stack_ + newCapacity;
+ }
+
+ void Destroy() {
+ Allocator::Free(stack_);
+ RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack
+ }
+
+ // Prohibit copy constructor & assignment operator.
+ Stack(const Stack&);
+ Stack& operator=(const Stack&);
+
+ Allocator* allocator_;
+ Allocator* ownAllocator_;
+ char *stack_;
+ char *stackTop_;
+ char *stackEnd_;
+ size_t initialCapacity_;
+};
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#if defined(__clang__)
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_STACK_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/strfunc.h b/ext/librethinkdbxx/src/rapidjson/internal/strfunc.h
new file mode 100644
index 00000000..2edfae52
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/strfunc.h
@@ -0,0 +1,55 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_
+#define RAPIDJSON_INTERNAL_STRFUNC_H_
+
+#include "../stream.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+//! Custom strlen() which works on different character types.
+/*! \tparam Ch Character type (e.g. char, wchar_t, short)
+ \param s Null-terminated input string.
+ \return Number of characters in the string.
+ \note This has the same semantics as strlen(), the return value is not number of Unicode codepoints.
+*/
+template <typename Ch>
+inline SizeType StrLen(const Ch* s) {
+ const Ch* p = s;
+ while (*p) ++p;
+ return SizeType(p - s);
+}
+
+//! Returns number of code points in a encoded string.
+template<typename Encoding>
+bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) {
+ GenericStringStream<Encoding> is(s);
+ const typename Encoding::Ch* end = s + length;
+ SizeType count = 0;
+ while (is.src_ < end) {
+ unsigned codepoint;
+ if (!Encoding::Decode(is, &codepoint))
+ return false;
+ count++;
+ }
+ *outCount = count;
+ return true;
+}
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_INTERNAL_STRFUNC_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/strtod.h b/ext/librethinkdbxx/src/rapidjson/internal/strtod.h
new file mode 100644
index 00000000..289c413b
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/strtod.h
@@ -0,0 +1,269 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_STRTOD_
+#define RAPIDJSON_STRTOD_
+
+#include "ieee754.h"
+#include "biginteger.h"
+#include "diyfp.h"
+#include "pow10.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+inline double FastPath(double significand, int exp) {
+ if (exp < -308)
+ return 0.0;
+ else if (exp >= 0)
+ return significand * internal::Pow10(exp);
+ else
+ return significand / internal::Pow10(-exp);
+}
+
+inline double StrtodNormalPrecision(double d, int p) {
+ if (p < -308) {
+ // Prevent expSum < -308, making Pow10(p) = 0
+ d = FastPath(d, -308);
+ d = FastPath(d, p + 308);
+ }
+ else
+ d = FastPath(d, p);
+ return d;
+}
+
+template <typename T>
+inline T Min3(T a, T b, T c) {
+ T m = a;
+ if (m > b) m = b;
+ if (m > c) m = c;
+ return m;
+}
+
+inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) {
+ const Double db(b);
+ const uint64_t bInt = db.IntegerSignificand();
+ const int bExp = db.IntegerExponent();
+ const int hExp = bExp - 1;
+
+ int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0;
+
+ // Adjust for decimal exponent
+ if (dExp >= 0) {
+ dS_Exp2 += dExp;
+ dS_Exp5 += dExp;
+ }
+ else {
+ bS_Exp2 -= dExp;
+ bS_Exp5 -= dExp;
+ hS_Exp2 -= dExp;
+ hS_Exp5 -= dExp;
+ }
+
+ // Adjust for binary exponent
+ if (bExp >= 0)
+ bS_Exp2 += bExp;
+ else {
+ dS_Exp2 -= bExp;
+ hS_Exp2 -= bExp;
+ }
+
+ // Adjust for half ulp exponent
+ if (hExp >= 0)
+ hS_Exp2 += hExp;
+ else {
+ dS_Exp2 -= hExp;
+ bS_Exp2 -= hExp;
+ }
+
+ // Remove common power of two factor from all three scaled values
+ int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2);
+ dS_Exp2 -= common_Exp2;
+ bS_Exp2 -= common_Exp2;
+ hS_Exp2 -= common_Exp2;
+
+ BigInteger dS = d;
+ dS.MultiplyPow5(static_cast<unsigned>(dS_Exp5)) <<= static_cast<unsigned>(dS_Exp2);
+
+ BigInteger bS(bInt);
+ bS.MultiplyPow5(static_cast<unsigned>(bS_Exp5)) <<= static_cast<unsigned>(bS_Exp2);
+
+ BigInteger hS(1);
+ hS.MultiplyPow5(static_cast<unsigned>(hS_Exp5)) <<= static_cast<unsigned>(hS_Exp2);
+
+ BigInteger delta(0);
+ dS.Difference(bS, &delta);
+
+ return delta.Compare(hS);
+}
+
+inline bool StrtodFast(double d, int p, double* result) {
+ // Use fast path for string-to-double conversion if possible
+ // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
+ if (p > 22 && p < 22 + 16) {
+ // Fast Path Cases In Disguise
+ d *= internal::Pow10(p - 22);
+ p = 22;
+ }
+
+ if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1
+ *result = FastPath(d, p);
+ return true;
+ }
+ else
+ return false;
+}
+
+// Compute an approximation and see if it is within 1/2 ULP
+inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) {
+ uint64_t significand = 0;
+ size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999
+ for (; i < length; i++) {
+ if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) ||
+ (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5'))
+ break;
+ significand = significand * 10u + static_cast<unsigned>(decimals[i] - '0');
+ }
+
+ if (i < length && decimals[i] >= '5') // Rounding
+ significand++;
+
+ size_t remaining = length - i;
+ const unsigned kUlpShift = 3;
+ const unsigned kUlp = 1 << kUlpShift;
+ int64_t error = (remaining == 0) ? 0 : kUlp / 2;
+
+ DiyFp v(significand, 0);
+ v = v.Normalize();
+ error <<= -v.e;
+
+ const int dExp = static_cast<int>(decimalPosition) - static_cast<int>(i) + exp;
+
+ int actualExp;
+ DiyFp cachedPower = GetCachedPower10(dExp, &actualExp);
+ if (actualExp != dExp) {
+ static const DiyFp kPow10[] = {
+ DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60), // 10^1
+ DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 00000000), -57), // 10^2
+ DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 00000000), -54), // 10^3
+ DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 00000000), -50), // 10^4
+ DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 00000000), -47), // 10^5
+ DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6
+ DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7
+ };
+ int adjustment = dExp - actualExp - 1;
+ RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7);
+ v = v * kPow10[adjustment];
+ if (length + static_cast<unsigned>(adjustment)> 19u) // has more digits than decimal digits in 64-bit
+ error += kUlp / 2;
+ }
+
+ v = v * cachedPower;
+
+ error += kUlp + (error == 0 ? 0 : 1);
+
+ const int oldExp = v.e;
+ v = v.Normalize();
+ error <<= oldExp - v.e;
+
+ const unsigned effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e);
+ unsigned precisionSize = 64 - effectiveSignificandSize;
+ if (precisionSize + kUlpShift >= 64) {
+ unsigned scaleExp = (precisionSize + kUlpShift) - 63;
+ v.f >>= scaleExp;
+ v.e += scaleExp;
+ error = (error >> scaleExp) + 1 + static_cast<int>(kUlp);
+ precisionSize -= scaleExp;
+ }
+
+ DiyFp rounded(v.f >> precisionSize, v.e + static_cast<int>(precisionSize));
+ const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp;
+ const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp;
+ if (precisionBits >= halfWay + static_cast<unsigned>(error)) {
+ rounded.f++;
+ if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340)
+ rounded.f >>= 1;
+ rounded.e++;
+ }
+ }
+
+ *result = rounded.ToDouble();
+
+ return halfWay - static_cast<unsigned>(error) >= precisionBits || precisionBits >= halfWay + static_cast<unsigned>(error);
+}
+
+inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) {
+ const BigInteger dInt(decimals, length);
+ const int dExp = static_cast<int>(decimalPosition) - static_cast<int>(length) + exp;
+ Double a(approx);
+ int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp);
+ if (cmp < 0)
+ return a.Value(); // within half ULP
+ else if (cmp == 0) {
+ // Round towards even
+ if (a.Significand() & 1)
+ return a.NextPositiveDouble();
+ else
+ return a.Value();
+ }
+ else // adjustment
+ return a.NextPositiveDouble();
+}
+
+inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) {
+ RAPIDJSON_ASSERT(d >= 0.0);
+ RAPIDJSON_ASSERT(length >= 1);
+
+ double result;
+ if (StrtodFast(d, p, &result))
+ return result;
+
+ // Trim leading zeros
+ while (*decimals == '0' && length > 1) {
+ length--;
+ decimals++;
+ decimalPosition--;
+ }
+
+ // Trim trailing zeros
+ while (decimals[length - 1] == '0' && length > 1) {
+ length--;
+ decimalPosition--;
+ exp++;
+ }
+
+ // Trim right-most digits
+ const int kMaxDecimalDigit = 780;
+ if (static_cast<int>(length) > kMaxDecimalDigit) {
+ int delta = (static_cast<int>(length) - kMaxDecimalDigit);
+ exp += delta;
+ decimalPosition -= static_cast<unsigned>(delta);
+ length = kMaxDecimalDigit;
+ }
+
+ // If too small, underflow to zero
+ if (int(length) + exp < -324)
+ return 0.0;
+
+ if (StrtodDiyFp(decimals, length, decimalPosition, exp, &result))
+ return result;
+
+ // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison
+ return StrtodBigInteger(result, decimals, length, decimalPosition, exp);
+}
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_STRTOD_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/swap.h b/ext/librethinkdbxx/src/rapidjson/internal/swap.h
new file mode 100644
index 00000000..666e49f9
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/swap.h
@@ -0,0 +1,46 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_INTERNAL_SWAP_H_
+#define RAPIDJSON_INTERNAL_SWAP_H_
+
+#include "../rapidjson.h"
+
+#if defined(__clang__)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+//! Custom swap() to avoid dependency on C++ <algorithm> header
+/*! \tparam T Type of the arguments to swap, should be instantiated with primitive C++ types only.
+ \note This has the same semantics as std::swap().
+*/
+template <typename T>
+inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT {
+ T tmp = a;
+ a = b;
+ b = tmp;
+}
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#if defined(__clang__)
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_INTERNAL_SWAP_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/istreamwrapper.h b/ext/librethinkdbxx/src/rapidjson/istreamwrapper.h
new file mode 100644
index 00000000..f5fe2897
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/istreamwrapper.h
@@ -0,0 +1,115 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_ISTREAMWRAPPER_H_
+#define RAPIDJSON_ISTREAMWRAPPER_H_
+
+#include "stream.h"
+#include <iosfwd>
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Wrapper of \c std::basic_istream into RapidJSON's Stream concept.
+/*!
+ The classes can be wrapped including but not limited to:
+
+ - \c std::istringstream
+ - \c std::stringstream
+ - \c std::wistringstream
+ - \c std::wstringstream
+ - \c std::ifstream
+ - \c std::fstream
+ - \c std::wifstream
+ - \c std::wfstream
+
+ \tparam StreamType Class derived from \c std::basic_istream.
+*/
+
+template <typename StreamType>
+class BasicIStreamWrapper {
+public:
+ typedef typename StreamType::char_type Ch;
+ BasicIStreamWrapper(StreamType& stream) : stream_(stream), count_(), peekBuffer_() {}
+
+ Ch Peek() const {
+ typename StreamType::int_type c = stream_.peek();
+ return RAPIDJSON_LIKELY(c != StreamType::traits_type::eof()) ? static_cast<Ch>(c) : '\0';
+ }
+
+ Ch Take() {
+ typename StreamType::int_type c = stream_.get();
+ if (RAPIDJSON_LIKELY(c != StreamType::traits_type::eof())) {
+ count_++;
+ return static_cast<Ch>(c);
+ }
+ else
+ return '\0';
+ }
+
+ // tellg() may return -1 when failed. So we count by ourself.
+ size_t Tell() const { return count_; }
+
+ Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ void Put(Ch) { RAPIDJSON_ASSERT(false); }
+ void Flush() { RAPIDJSON_ASSERT(false); }
+ size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+ // For encoding detection only.
+ const Ch* Peek4() const {
+ RAPIDJSON_ASSERT(sizeof(Ch) == 1); // Only usable for byte stream.
+ int i;
+ bool hasError = false;
+ for (i = 0; i < 4; ++i) {
+ typename StreamType::int_type c = stream_.get();
+ if (c == StreamType::traits_type::eof()) {
+ hasError = true;
+ stream_.clear();
+ break;
+ }
+ peekBuffer_[i] = static_cast<Ch>(c);
+ }
+ for (--i; i >= 0; --i)
+ stream_.putback(peekBuffer_[i]);
+ return !hasError ? peekBuffer_ : 0;
+ }
+
+private:
+ BasicIStreamWrapper(const BasicIStreamWrapper&);
+ BasicIStreamWrapper& operator=(const BasicIStreamWrapper&);
+
+ StreamType& stream_;
+ size_t count_; //!< Number of characters read. Note:
+ mutable Ch peekBuffer_[4];
+};
+
+typedef BasicIStreamWrapper<std::istream> IStreamWrapper;
+typedef BasicIStreamWrapper<std::wistream> WIStreamWrapper;
+
+#if defined(__clang__) || defined(_MSC_VER)
+RAPIDJSON_DIAG_POP
+#endif
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_ISTREAMWRAPPER_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/memorybuffer.h b/ext/librethinkdbxx/src/rapidjson/memorybuffer.h
new file mode 100644
index 00000000..39bee1de
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/memorybuffer.h
@@ -0,0 +1,70 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_MEMORYBUFFER_H_
+#define RAPIDJSON_MEMORYBUFFER_H_
+
+#include "stream.h"
+#include "internal/stack.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Represents an in-memory output byte stream.
+/*!
+ This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream.
+
+ It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file.
+
+ Differences between MemoryBuffer and StringBuffer:
+ 1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer.
+ 2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator.
+
+ \tparam Allocator type for allocating memory buffer.
+ \note implements Stream concept
+*/
+template <typename Allocator = CrtAllocator>
+struct GenericMemoryBuffer {
+ typedef char Ch; // byte
+
+ GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {}
+
+ void Put(Ch c) { *stack_.template Push<Ch>() = c; }
+ void Flush() {}
+
+ void Clear() { stack_.Clear(); }
+ void ShrinkToFit() { stack_.ShrinkToFit(); }
+ Ch* Push(size_t count) { return stack_.template Push<Ch>(count); }
+ void Pop(size_t count) { stack_.template Pop<Ch>(count); }
+
+ const Ch* GetBuffer() const {
+ return stack_.template Bottom<Ch>();
+ }
+
+ size_t GetSize() const { return stack_.GetSize(); }
+
+ static const size_t kDefaultCapacity = 256;
+ mutable internal::Stack<Allocator> stack_;
+};
+
+typedef GenericMemoryBuffer<> MemoryBuffer;
+
+//! Implement specialized version of PutN() with memset() for better performance.
+template<>
+inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) {
+ std::memset(memoryBuffer.stack_.Push<char>(n), c, n * sizeof(c));
+}
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_MEMORYBUFFER_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/memorystream.h b/ext/librethinkdbxx/src/rapidjson/memorystream.h
new file mode 100644
index 00000000..1d71d8a4
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/memorystream.h
@@ -0,0 +1,71 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_MEMORYSTREAM_H_
+#define RAPIDJSON_MEMORYSTREAM_H_
+
+#include "stream.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(unreachable-code)
+RAPIDJSON_DIAG_OFF(missing-noreturn)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Represents an in-memory input byte stream.
+/*!
+ This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream.
+
+ It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file.
+
+ Differences between MemoryStream and StringStream:
+ 1. StringStream has encoding but MemoryStream is a byte stream.
+ 2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source.
+ 3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4().
+ \note implements Stream concept
+*/
+struct MemoryStream {
+ typedef char Ch; // byte
+
+ MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {}
+
+ Ch Peek() const { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; }
+ Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; }
+ size_t Tell() const { return static_cast<size_t>(src_ - begin_); }
+
+ Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ void Put(Ch) { RAPIDJSON_ASSERT(false); }
+ void Flush() { RAPIDJSON_ASSERT(false); }
+ size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+ // For encoding detection only.
+ const Ch* Peek4() const {
+ return Tell() + 4 <= size_ ? src_ : 0;
+ }
+
+ const Ch* src_; //!< Current read position.
+ const Ch* begin_; //!< Original head of the string.
+ const Ch* end_; //!< End of stream.
+ size_t size_; //!< Size of the stream.
+};
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_MEMORYBUFFER_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/msinttypes/inttypes.h b/ext/librethinkdbxx/src/rapidjson/msinttypes/inttypes.h
new file mode 100644
index 00000000..18111286
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/msinttypes/inttypes.h
@@ -0,0 +1,316 @@
+// ISO C9x compliant inttypes.h for Microsoft Visual Studio
+// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
+//
+// Copyright (c) 2006-2013 Alexander Chemeris
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the product nor the names of its contributors may
+// be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+// The above software in this distribution may have been modified by
+// THL A29 Limited ("Tencent Modifications").
+// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited.
+
+#ifndef _MSC_VER // [
+#error "Use this header only with Microsoft Visual C++ compilers!"
+#endif // _MSC_VER ]
+
+#ifndef _MSC_INTTYPES_H_ // [
+#define _MSC_INTTYPES_H_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#include "stdint.h"
+
+// miloyip: VC supports inttypes.h since VC2013
+#if _MSC_VER >= 1800
+#include <inttypes.h>
+#else
+
+// 7.8 Format conversion of integer types
+
+typedef struct {
+ intmax_t quot;
+ intmax_t rem;
+} imaxdiv_t;
+
+// 7.8.1 Macros for format specifiers
+
+#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198
+
+// The fprintf macros for signed integers are:
+#define PRId8 "d"
+#define PRIi8 "i"
+#define PRIdLEAST8 "d"
+#define PRIiLEAST8 "i"
+#define PRIdFAST8 "d"
+#define PRIiFAST8 "i"
+
+#define PRId16 "hd"
+#define PRIi16 "hi"
+#define PRIdLEAST16 "hd"
+#define PRIiLEAST16 "hi"
+#define PRIdFAST16 "hd"
+#define PRIiFAST16 "hi"
+
+#define PRId32 "I32d"
+#define PRIi32 "I32i"
+#define PRIdLEAST32 "I32d"
+#define PRIiLEAST32 "I32i"
+#define PRIdFAST32 "I32d"
+#define PRIiFAST32 "I32i"
+
+#define PRId64 "I64d"
+#define PRIi64 "I64i"
+#define PRIdLEAST64 "I64d"
+#define PRIiLEAST64 "I64i"
+#define PRIdFAST64 "I64d"
+#define PRIiFAST64 "I64i"
+
+#define PRIdMAX "I64d"
+#define PRIiMAX "I64i"
+
+#define PRIdPTR "Id"
+#define PRIiPTR "Ii"
+
+// The fprintf macros for unsigned integers are:
+#define PRIo8 "o"
+#define PRIu8 "u"
+#define PRIx8 "x"
+#define PRIX8 "X"
+#define PRIoLEAST8 "o"
+#define PRIuLEAST8 "u"
+#define PRIxLEAST8 "x"
+#define PRIXLEAST8 "X"
+#define PRIoFAST8 "o"
+#define PRIuFAST8 "u"
+#define PRIxFAST8 "x"
+#define PRIXFAST8 "X"
+
+#define PRIo16 "ho"
+#define PRIu16 "hu"
+#define PRIx16 "hx"
+#define PRIX16 "hX"
+#define PRIoLEAST16 "ho"
+#define PRIuLEAST16 "hu"
+#define PRIxLEAST16 "hx"
+#define PRIXLEAST16 "hX"
+#define PRIoFAST16 "ho"
+#define PRIuFAST16 "hu"
+#define PRIxFAST16 "hx"
+#define PRIXFAST16 "hX"
+
+#define PRIo32 "I32o"
+#define PRIu32 "I32u"
+#define PRIx32 "I32x"
+#define PRIX32 "I32X"
+#define PRIoLEAST32 "I32o"
+#define PRIuLEAST32 "I32u"
+#define PRIxLEAST32 "I32x"
+#define PRIXLEAST32 "I32X"
+#define PRIoFAST32 "I32o"
+#define PRIuFAST32 "I32u"
+#define PRIxFAST32 "I32x"
+#define PRIXFAST32 "I32X"
+
+#define PRIo64 "I64o"
+#define PRIu64 "I64u"
+#define PRIx64 "I64x"
+#define PRIX64 "I64X"
+#define PRIoLEAST64 "I64o"
+#define PRIuLEAST64 "I64u"
+#define PRIxLEAST64 "I64x"
+#define PRIXLEAST64 "I64X"
+#define PRIoFAST64 "I64o"
+#define PRIuFAST64 "I64u"
+#define PRIxFAST64 "I64x"
+#define PRIXFAST64 "I64X"
+
+#define PRIoMAX "I64o"
+#define PRIuMAX "I64u"
+#define PRIxMAX "I64x"
+#define PRIXMAX "I64X"
+
+#define PRIoPTR "Io"
+#define PRIuPTR "Iu"
+#define PRIxPTR "Ix"
+#define PRIXPTR "IX"
+
+// The fscanf macros for signed integers are:
+#define SCNd8 "d"
+#define SCNi8 "i"
+#define SCNdLEAST8 "d"
+#define SCNiLEAST8 "i"
+#define SCNdFAST8 "d"
+#define SCNiFAST8 "i"
+
+#define SCNd16 "hd"
+#define SCNi16 "hi"
+#define SCNdLEAST16 "hd"
+#define SCNiLEAST16 "hi"
+#define SCNdFAST16 "hd"
+#define SCNiFAST16 "hi"
+
+#define SCNd32 "ld"
+#define SCNi32 "li"
+#define SCNdLEAST32 "ld"
+#define SCNiLEAST32 "li"
+#define SCNdFAST32 "ld"
+#define SCNiFAST32 "li"
+
+#define SCNd64 "I64d"
+#define SCNi64 "I64i"
+#define SCNdLEAST64 "I64d"
+#define SCNiLEAST64 "I64i"
+#define SCNdFAST64 "I64d"
+#define SCNiFAST64 "I64i"
+
+#define SCNdMAX "I64d"
+#define SCNiMAX "I64i"
+
+#ifdef _WIN64 // [
+# define SCNdPTR "I64d"
+# define SCNiPTR "I64i"
+#else // _WIN64 ][
+# define SCNdPTR "ld"
+# define SCNiPTR "li"
+#endif // _WIN64 ]
+
+// The fscanf macros for unsigned integers are:
+#define SCNo8 "o"
+#define SCNu8 "u"
+#define SCNx8 "x"
+#define SCNX8 "X"
+#define SCNoLEAST8 "o"
+#define SCNuLEAST8 "u"
+#define SCNxLEAST8 "x"
+#define SCNXLEAST8 "X"
+#define SCNoFAST8 "o"
+#define SCNuFAST8 "u"
+#define SCNxFAST8 "x"
+#define SCNXFAST8 "X"
+
+#define SCNo16 "ho"
+#define SCNu16 "hu"
+#define SCNx16 "hx"
+#define SCNX16 "hX"
+#define SCNoLEAST16 "ho"
+#define SCNuLEAST16 "hu"
+#define SCNxLEAST16 "hx"
+#define SCNXLEAST16 "hX"
+#define SCNoFAST16 "ho"
+#define SCNuFAST16 "hu"
+#define SCNxFAST16 "hx"
+#define SCNXFAST16 "hX"
+
+#define SCNo32 "lo"
+#define SCNu32 "lu"
+#define SCNx32 "lx"
+#define SCNX32 "lX"
+#define SCNoLEAST32 "lo"
+#define SCNuLEAST32 "lu"
+#define SCNxLEAST32 "lx"
+#define SCNXLEAST32 "lX"
+#define SCNoFAST32 "lo"
+#define SCNuFAST32 "lu"
+#define SCNxFAST32 "lx"
+#define SCNXFAST32 "lX"
+
+#define SCNo64 "I64o"
+#define SCNu64 "I64u"
+#define SCNx64 "I64x"
+#define SCNX64 "I64X"
+#define SCNoLEAST64 "I64o"
+#define SCNuLEAST64 "I64u"
+#define SCNxLEAST64 "I64x"
+#define SCNXLEAST64 "I64X"
+#define SCNoFAST64 "I64o"
+#define SCNuFAST64 "I64u"
+#define SCNxFAST64 "I64x"
+#define SCNXFAST64 "I64X"
+
+#define SCNoMAX "I64o"
+#define SCNuMAX "I64u"
+#define SCNxMAX "I64x"
+#define SCNXMAX "I64X"
+
+#ifdef _WIN64 // [
+# define SCNoPTR "I64o"
+# define SCNuPTR "I64u"
+# define SCNxPTR "I64x"
+# define SCNXPTR "I64X"
+#else // _WIN64 ][
+# define SCNoPTR "lo"
+# define SCNuPTR "lu"
+# define SCNxPTR "lx"
+# define SCNXPTR "lX"
+#endif // _WIN64 ]
+
+#endif // __STDC_FORMAT_MACROS ]
+
+// 7.8.2 Functions for greatest-width integer types
+
+// 7.8.2.1 The imaxabs function
+#define imaxabs _abs64
+
+// 7.8.2.2 The imaxdiv function
+
+// This is modified version of div() function from Microsoft's div.c found
+// in %MSVC.NET%\crt\src\div.c
+#ifdef STATIC_IMAXDIV // [
+static
+#else // STATIC_IMAXDIV ][
+_inline
+#endif // STATIC_IMAXDIV ]
+imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom)
+{
+ imaxdiv_t result;
+
+ result.quot = numer / denom;
+ result.rem = numer % denom;
+
+ if (numer < 0 && result.rem > 0) {
+ // did division wrong; must fix up
+ ++result.quot;
+ result.rem -= denom;
+ }
+
+ return result;
+}
+
+// 7.8.2.3 The strtoimax and strtoumax functions
+#define strtoimax _strtoi64
+#define strtoumax _strtoui64
+
+// 7.8.2.4 The wcstoimax and wcstoumax functions
+#define wcstoimax _wcstoi64
+#define wcstoumax _wcstoui64
+
+#endif // _MSC_VER >= 1800
+
+#endif // _MSC_INTTYPES_H_ ]
diff --git a/ext/librethinkdbxx/src/rapidjson/msinttypes/stdint.h b/ext/librethinkdbxx/src/rapidjson/msinttypes/stdint.h
new file mode 100644
index 00000000..3d4477b9
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/msinttypes/stdint.h
@@ -0,0 +1,300 @@
+// ISO C9x compliant stdint.h for Microsoft Visual Studio
+// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
+//
+// Copyright (c) 2006-2013 Alexander Chemeris
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the product nor the names of its contributors may
+// be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+// The above software in this distribution may have been modified by
+// THL A29 Limited ("Tencent Modifications").
+// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited.
+
+#ifndef _MSC_VER // [
+#error "Use this header only with Microsoft Visual C++ compilers!"
+#endif // _MSC_VER ]
+
+#ifndef _MSC_STDINT_H_ // [
+#define _MSC_STDINT_H_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010.
+#if _MSC_VER >= 1600 // [
+#include <stdint.h>
+
+#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
+
+#undef INT8_C
+#undef INT16_C
+#undef INT32_C
+#undef INT64_C
+#undef UINT8_C
+#undef UINT16_C
+#undef UINT32_C
+#undef UINT64_C
+
+// 7.18.4.1 Macros for minimum-width integer constants
+
+#define INT8_C(val) val##i8
+#define INT16_C(val) val##i16
+#define INT32_C(val) val##i32
+#define INT64_C(val) val##i64
+
+#define UINT8_C(val) val##ui8
+#define UINT16_C(val) val##ui16
+#define UINT32_C(val) val##ui32
+#define UINT64_C(val) val##ui64
+
+// 7.18.4.2 Macros for greatest-width integer constants
+// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>.
+// Check out Issue 9 for the details.
+#ifndef INTMAX_C // [
+# define INTMAX_C INT64_C
+#endif // INTMAX_C ]
+#ifndef UINTMAX_C // [
+# define UINTMAX_C UINT64_C
+#endif // UINTMAX_C ]
+
+#endif // __STDC_CONSTANT_MACROS ]
+
+#else // ] _MSC_VER >= 1700 [
+
+#include <limits.h>
+
+// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
+// compiling for ARM we have to wrap <wchar.h> include with 'extern "C++" {}'
+// or compiler would give many errors like this:
+// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
+#if defined(__cplusplus) && !defined(_M_ARM)
+extern "C" {
+#endif
+# include <wchar.h>
+#if defined(__cplusplus) && !defined(_M_ARM)
+}
+#endif
+
+// Define _W64 macros to mark types changing their size, like intptr_t.
+#ifndef _W64
+# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
+# define _W64 __w64
+# else
+# define _W64
+# endif
+#endif
+
+
+// 7.18.1 Integer types
+
+// 7.18.1.1 Exact-width integer types
+
+// Visual Studio 6 and Embedded Visual C++ 4 doesn't
+// realize that, e.g. char has the same size as __int8
+// so we give up on __intX for them.
+#if (_MSC_VER < 1300)
+ typedef signed char int8_t;
+ typedef signed short int16_t;
+ typedef signed int int32_t;
+ typedef unsigned char uint8_t;
+ typedef unsigned short uint16_t;
+ typedef unsigned int uint32_t;
+#else
+ typedef signed __int8 int8_t;
+ typedef signed __int16 int16_t;
+ typedef signed __int32 int32_t;
+ typedef unsigned __int8 uint8_t;
+ typedef unsigned __int16 uint16_t;
+ typedef unsigned __int32 uint32_t;
+#endif
+typedef signed __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+
+
+// 7.18.1.2 Minimum-width integer types
+typedef int8_t int_least8_t;
+typedef int16_t int_least16_t;
+typedef int32_t int_least32_t;
+typedef int64_t int_least64_t;
+typedef uint8_t uint_least8_t;
+typedef uint16_t uint_least16_t;
+typedef uint32_t uint_least32_t;
+typedef uint64_t uint_least64_t;
+
+// 7.18.1.3 Fastest minimum-width integer types
+typedef int8_t int_fast8_t;
+typedef int16_t int_fast16_t;
+typedef int32_t int_fast32_t;
+typedef int64_t int_fast64_t;
+typedef uint8_t uint_fast8_t;
+typedef uint16_t uint_fast16_t;
+typedef uint32_t uint_fast32_t;
+typedef uint64_t uint_fast64_t;
+
+// 7.18.1.4 Integer types capable of holding object pointers
+#ifdef _WIN64 // [
+ typedef signed __int64 intptr_t;
+ typedef unsigned __int64 uintptr_t;
+#else // _WIN64 ][
+ typedef _W64 signed int intptr_t;
+ typedef _W64 unsigned int uintptr_t;
+#endif // _WIN64 ]
+
+// 7.18.1.5 Greatest-width integer types
+typedef int64_t intmax_t;
+typedef uint64_t uintmax_t;
+
+
+// 7.18.2 Limits of specified-width integer types
+
+#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
+
+// 7.18.2.1 Limits of exact-width integer types
+#define INT8_MIN ((int8_t)_I8_MIN)
+#define INT8_MAX _I8_MAX
+#define INT16_MIN ((int16_t)_I16_MIN)
+#define INT16_MAX _I16_MAX
+#define INT32_MIN ((int32_t)_I32_MIN)
+#define INT32_MAX _I32_MAX
+#define INT64_MIN ((int64_t)_I64_MIN)
+#define INT64_MAX _I64_MAX
+#define UINT8_MAX _UI8_MAX
+#define UINT16_MAX _UI16_MAX
+#define UINT32_MAX _UI32_MAX
+#define UINT64_MAX _UI64_MAX
+
+// 7.18.2.2 Limits of minimum-width integer types
+#define INT_LEAST8_MIN INT8_MIN
+#define INT_LEAST8_MAX INT8_MAX
+#define INT_LEAST16_MIN INT16_MIN
+#define INT_LEAST16_MAX INT16_MAX
+#define INT_LEAST32_MIN INT32_MIN
+#define INT_LEAST32_MAX INT32_MAX
+#define INT_LEAST64_MIN INT64_MIN
+#define INT_LEAST64_MAX INT64_MAX
+#define UINT_LEAST8_MAX UINT8_MAX
+#define UINT_LEAST16_MAX UINT16_MAX
+#define UINT_LEAST32_MAX UINT32_MAX
+#define UINT_LEAST64_MAX UINT64_MAX
+
+// 7.18.2.3 Limits of fastest minimum-width integer types
+#define INT_FAST8_MIN INT8_MIN
+#define INT_FAST8_MAX INT8_MAX
+#define INT_FAST16_MIN INT16_MIN
+#define INT_FAST16_MAX INT16_MAX
+#define INT_FAST32_MIN INT32_MIN
+#define INT_FAST32_MAX INT32_MAX
+#define INT_FAST64_MIN INT64_MIN
+#define INT_FAST64_MAX INT64_MAX
+#define UINT_FAST8_MAX UINT8_MAX
+#define UINT_FAST16_MAX UINT16_MAX
+#define UINT_FAST32_MAX UINT32_MAX
+#define UINT_FAST64_MAX UINT64_MAX
+
+// 7.18.2.4 Limits of integer types capable of holding object pointers
+#ifdef _WIN64 // [
+# define INTPTR_MIN INT64_MIN
+# define INTPTR_MAX INT64_MAX
+# define UINTPTR_MAX UINT64_MAX
+#else // _WIN64 ][
+# define INTPTR_MIN INT32_MIN
+# define INTPTR_MAX INT32_MAX
+# define UINTPTR_MAX UINT32_MAX
+#endif // _WIN64 ]
+
+// 7.18.2.5 Limits of greatest-width integer types
+#define INTMAX_MIN INT64_MIN
+#define INTMAX_MAX INT64_MAX
+#define UINTMAX_MAX UINT64_MAX
+
+// 7.18.3 Limits of other integer types
+
+#ifdef _WIN64 // [
+# define PTRDIFF_MIN _I64_MIN
+# define PTRDIFF_MAX _I64_MAX
+#else // _WIN64 ][
+# define PTRDIFF_MIN _I32_MIN
+# define PTRDIFF_MAX _I32_MAX
+#endif // _WIN64 ]
+
+#define SIG_ATOMIC_MIN INT_MIN
+#define SIG_ATOMIC_MAX INT_MAX
+
+#ifndef SIZE_MAX // [
+# ifdef _WIN64 // [
+# define SIZE_MAX _UI64_MAX
+# else // _WIN64 ][
+# define SIZE_MAX _UI32_MAX
+# endif // _WIN64 ]
+#endif // SIZE_MAX ]
+
+// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
+#ifndef WCHAR_MIN // [
+# define WCHAR_MIN 0
+#endif // WCHAR_MIN ]
+#ifndef WCHAR_MAX // [
+# define WCHAR_MAX _UI16_MAX
+#endif // WCHAR_MAX ]
+
+#define WINT_MIN 0
+#define WINT_MAX _UI16_MAX
+
+#endif // __STDC_LIMIT_MACROS ]
+
+
+// 7.18.4 Limits of other integer types
+
+#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
+
+// 7.18.4.1 Macros for minimum-width integer constants
+
+#define INT8_C(val) val##i8
+#define INT16_C(val) val##i16
+#define INT32_C(val) val##i32
+#define INT64_C(val) val##i64
+
+#define UINT8_C(val) val##ui8
+#define UINT16_C(val) val##ui16
+#define UINT32_C(val) val##ui32
+#define UINT64_C(val) val##ui64
+
+// 7.18.4.2 Macros for greatest-width integer constants
+// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>.
+// Check out Issue 9 for the details.
+#ifndef INTMAX_C // [
+# define INTMAX_C INT64_C
+#endif // INTMAX_C ]
+#ifndef UINTMAX_C // [
+# define UINTMAX_C UINT64_C
+#endif // UINTMAX_C ]
+
+#endif // __STDC_CONSTANT_MACROS ]
+
+#endif // _MSC_VER >= 1600 ]
+
+#endif // _MSC_STDINT_H_ ]
diff --git a/ext/librethinkdbxx/src/rapidjson/ostreamwrapper.h b/ext/librethinkdbxx/src/rapidjson/ostreamwrapper.h
new file mode 100644
index 00000000..6f4667c0
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/ostreamwrapper.h
@@ -0,0 +1,81 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_OSTREAMWRAPPER_H_
+#define RAPIDJSON_OSTREAMWRAPPER_H_
+
+#include "stream.h"
+#include <iosfwd>
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept.
+/*!
+ The classes can be wrapped including but not limited to:
+
+ - \c std::ostringstream
+ - \c std::stringstream
+ - \c std::wpstringstream
+ - \c std::wstringstream
+ - \c std::ifstream
+ - \c std::fstream
+ - \c std::wofstream
+ - \c std::wfstream
+
+ \tparam StreamType Class derived from \c std::basic_ostream.
+*/
+
+template <typename StreamType>
+class BasicOStreamWrapper {
+public:
+ typedef typename StreamType::char_type Ch;
+ BasicOStreamWrapper(StreamType& stream) : stream_(stream) {}
+
+ void Put(Ch c) {
+ stream_.put(c);
+ }
+
+ void Flush() {
+ stream_.flush();
+ }
+
+ // Not implemented
+ char Peek() const { RAPIDJSON_ASSERT(false); return 0; }
+ char Take() { RAPIDJSON_ASSERT(false); return 0; }
+ size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
+ char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; }
+
+private:
+ BasicOStreamWrapper(const BasicOStreamWrapper&);
+ BasicOStreamWrapper& operator=(const BasicOStreamWrapper&);
+
+ StreamType& stream_;
+};
+
+typedef BasicOStreamWrapper<std::ostream> OStreamWrapper;
+typedef BasicOStreamWrapper<std::wostream> WOStreamWrapper;
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_OSTREAMWRAPPER_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/pointer.h b/ext/librethinkdbxx/src/rapidjson/pointer.h
new file mode 100644
index 00000000..0206ac1c
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/pointer.h
@@ -0,0 +1,1358 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_POINTER_H_
+#define RAPIDJSON_POINTER_H_
+
+#include "document.h"
+#include "internal/itoa.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(switch-enum)
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+static const SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token
+
+//! Error code of parsing.
+/*! \ingroup RAPIDJSON_ERRORS
+ \see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode
+*/
+enum PointerParseErrorCode {
+ kPointerParseErrorNone = 0, //!< The parse is successful
+
+ kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/'
+ kPointerParseErrorInvalidEscape, //!< Invalid escape
+ kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment
+ kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericPointer
+
+//! Represents a JSON Pointer. Use Pointer for UTF8 encoding and default allocator.
+/*!
+ This class implements RFC 6901 "JavaScript Object Notation (JSON) Pointer"
+ (https://tools.ietf.org/html/rfc6901).
+
+ A JSON pointer is for identifying a specific value in a JSON document
+ (GenericDocument). It can simplify coding of DOM tree manipulation, because it
+ can access multiple-level depth of DOM tree with single API call.
+
+ After it parses a string representation (e.g. "/foo/0" or URI fragment
+ representation (e.g. "#/foo/0") into its internal representation (tokens),
+ it can be used to resolve a specific value in multiple documents, or sub-tree
+ of documents.
+
+ Contrary to GenericValue, Pointer can be copy constructed and copy assigned.
+ Apart from assignment, a Pointer cannot be modified after construction.
+
+ Although Pointer is very convenient, please aware that constructing Pointer
+ involves parsing and dynamic memory allocation. A special constructor with user-
+ supplied tokens eliminates these.
+
+ GenericPointer depends on GenericDocument and GenericValue.
+
+ \tparam ValueType The value type of the DOM tree. E.g. GenericValue<UTF8<> >
+ \tparam Allocator The allocator type for allocating memory for internal representation.
+
+ \note GenericPointer uses same encoding of ValueType.
+ However, Allocator of GenericPointer is independent of Allocator of Value.
+*/
+template <typename ValueType, typename Allocator = CrtAllocator>
+class GenericPointer {
+public:
+ typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value
+ typedef typename ValueType::Ch Ch; //!< Character type from Value
+
+ //! A token is the basic units of internal representation.
+ /*!
+ A JSON pointer string representation "/foo/123" is parsed to two tokens:
+ "foo" and 123. 123 will be represented in both numeric form and string form.
+ They are resolved according to the actual value type (object or array).
+
+ For token that are not numbers, or the numeric value is out of bound
+ (greater than limits of SizeType), they are only treated as string form
+ (i.e. the token's index will be equal to kPointerInvalidIndex).
+
+ This struct is public so that user can create a Pointer without parsing and
+ allocation, using a special constructor.
+ */
+ struct Token {
+ const Ch* name; //!< Name of the token. It has null character at the end but it can contain null character.
+ SizeType length; //!< Length of the name.
+ SizeType index; //!< A valid array index, if it is not equal to kPointerInvalidIndex.
+ };
+
+ //!@name Constructors and destructor.
+ //@{
+
+ //! Default constructor.
+ GenericPointer(Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {}
+
+ //! Constructor that parses a string or URI fragment representation.
+ /*!
+ \param source A null-terminated, string or URI fragment representation of JSON pointer.
+ \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one.
+ */
+ explicit GenericPointer(const Ch* source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {
+ Parse(source, internal::StrLen(source));
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Constructor that parses a string or URI fragment representation.
+ /*!
+ \param source A string or URI fragment representation of JSON pointer.
+ \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one.
+ \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING.
+ */
+ explicit GenericPointer(const std::basic_string<Ch>& source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {
+ Parse(source.c_str(), source.size());
+ }
+#endif
+
+ //! Constructor that parses a string or URI fragment representation, with length of the source string.
+ /*!
+ \param source A string or URI fragment representation of JSON pointer.
+ \param length Length of source.
+ \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one.
+ \note Slightly faster than the overload without length.
+ */
+ GenericPointer(const Ch* source, size_t length, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {
+ Parse(source, length);
+ }
+
+ //! Constructor with user-supplied tokens.
+ /*!
+ This constructor let user supplies const array of tokens.
+ This prevents the parsing process and eliminates allocation.
+ This is preferred for memory constrained environments.
+
+ \param tokens An constant array of tokens representing the JSON pointer.
+ \param tokenCount Number of tokens.
+
+ \b Example
+ \code
+ #define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex }
+ #define INDEX(i) { #i, sizeof(#i) - 1, i }
+
+ static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(123) };
+ static const Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0]));
+ // Equivalent to static const Pointer p("/foo/123");
+
+ #undef NAME
+ #undef INDEX
+ \endcode
+ */
+ GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast<Token*>(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {}
+
+ //! Copy constructor.
+ GenericPointer(const GenericPointer& rhs, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {
+ *this = rhs;
+ }
+
+ //! Destructor.
+ ~GenericPointer() {
+ if (nameBuffer_) // If user-supplied tokens constructor is used, nameBuffer_ is nullptr and tokens_ are not deallocated.
+ Allocator::Free(tokens_);
+ RAPIDJSON_DELETE(ownAllocator_);
+ }
+
+ //! Assignment operator.
+ GenericPointer& operator=(const GenericPointer& rhs) {
+ if (this != &rhs) {
+ // Do not delete ownAllcator
+ if (nameBuffer_)
+ Allocator::Free(tokens_);
+
+ tokenCount_ = rhs.tokenCount_;
+ parseErrorOffset_ = rhs.parseErrorOffset_;
+ parseErrorCode_ = rhs.parseErrorCode_;
+
+ if (rhs.nameBuffer_)
+ CopyFromRaw(rhs); // Normally parsed tokens.
+ else {
+ tokens_ = rhs.tokens_; // User supplied const tokens.
+ nameBuffer_ = 0;
+ }
+ }
+ return *this;
+ }
+
+ //@}
+
+ //!@name Append token
+ //@{
+
+ //! Append a token and return a new Pointer
+ /*!
+ \param token Token to be appended.
+ \param allocator Allocator for the newly return Pointer.
+ \return A new Pointer with appended token.
+ */
+ GenericPointer Append(const Token& token, Allocator* allocator = 0) const {
+ GenericPointer r;
+ r.allocator_ = allocator;
+ Ch *p = r.CopyFromRaw(*this, 1, token.length + 1);
+ std::memcpy(p, token.name, (token.length + 1) * sizeof(Ch));
+ r.tokens_[tokenCount_].name = p;
+ r.tokens_[tokenCount_].length = token.length;
+ r.tokens_[tokenCount_].index = token.index;
+ return r;
+ }
+
+ //! Append a name token with length, and return a new Pointer
+ /*!
+ \param name Name to be appended.
+ \param length Length of name.
+ \param allocator Allocator for the newly return Pointer.
+ \return A new Pointer with appended token.
+ */
+ GenericPointer Append(const Ch* name, SizeType length, Allocator* allocator = 0) const {
+ Token token = { name, length, kPointerInvalidIndex };
+ return Append(token, allocator);
+ }
+
+ //! Append a name token without length, and return a new Pointer
+ /*!
+ \param name Name (const Ch*) to be appended.
+ \param allocator Allocator for the newly return Pointer.
+ \return A new Pointer with appended token.
+ */
+ template <typename T>
+ RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr<internal::IsSame<typename internal::RemoveConst<T>::Type, Ch> >), (GenericPointer))
+ Append(T* name, Allocator* allocator = 0) const {
+ return Append(name, StrLen(name), allocator);
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Append a name token, and return a new Pointer
+ /*!
+ \param name Name to be appended.
+ \param allocator Allocator for the newly return Pointer.
+ \return A new Pointer with appended token.
+ */
+ GenericPointer Append(const std::basic_string<Ch>& name, Allocator* allocator = 0) const {
+ return Append(name.c_str(), static_cast<SizeType>(name.size()), allocator);
+ }
+#endif
+
+ //! Append a index token, and return a new Pointer
+ /*!
+ \param index Index to be appended.
+ \param allocator Allocator for the newly return Pointer.
+ \return A new Pointer with appended token.
+ */
+ GenericPointer Append(SizeType index, Allocator* allocator = 0) const {
+ char buffer[21];
+ char* end = sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer);
+ SizeType length = static_cast<SizeType>(end - buffer);
+ buffer[length] = '\0';
+
+ if (sizeof(Ch) == 1) {
+ Token token = { reinterpret_cast<Ch*>(buffer), length, index };
+ return Append(token, allocator);
+ }
+ else {
+ Ch name[21];
+ for (size_t i = 0; i <= length; i++)
+ name[i] = buffer[i];
+ Token token = { name, length, index };
+ return Append(token, allocator);
+ }
+ }
+
+ //! Append a token by value, and return a new Pointer
+ /*!
+ \param token token to be appended.
+ \param allocator Allocator for the newly return Pointer.
+ \return A new Pointer with appended token.
+ */
+ GenericPointer Append(const ValueType& token, Allocator* allocator = 0) const {
+ if (token.IsString())
+ return Append(token.GetString(), token.GetStringLength(), allocator);
+ else {
+ RAPIDJSON_ASSERT(token.IsUint64());
+ RAPIDJSON_ASSERT(token.GetUint64() <= SizeType(~0));
+ return Append(static_cast<SizeType>(token.GetUint64()), allocator);
+ }
+ }
+
+ //!@name Handling Parse Error
+ //@{
+
+ //! Check whether this is a valid pointer.
+ bool IsValid() const { return parseErrorCode_ == kPointerParseErrorNone; }
+
+ //! Get the parsing error offset in code unit.
+ size_t GetParseErrorOffset() const { return parseErrorOffset_; }
+
+ //! Get the parsing error code.
+ PointerParseErrorCode GetParseErrorCode() const { return parseErrorCode_; }
+
+ //@}
+
+ //! Get the allocator of this pointer.
+ Allocator& GetAllocator() { return *allocator_; }
+
+ //!@name Tokens
+ //@{
+
+ //! Get the token array (const version only).
+ const Token* GetTokens() const { return tokens_; }
+
+ //! Get the number of tokens.
+ size_t GetTokenCount() const { return tokenCount_; }
+
+ //@}
+
+ //!@name Equality/inequality operators
+ //@{
+
+ //! Equality operator.
+ /*!
+ \note When any pointers are invalid, always returns false.
+ */
+ bool operator==(const GenericPointer& rhs) const {
+ if (!IsValid() || !rhs.IsValid() || tokenCount_ != rhs.tokenCount_)
+ return false;
+
+ for (size_t i = 0; i < tokenCount_; i++) {
+ if (tokens_[i].index != rhs.tokens_[i].index ||
+ tokens_[i].length != rhs.tokens_[i].length ||
+ (tokens_[i].length != 0 && std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch)* tokens_[i].length) != 0))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ //! Inequality operator.
+ /*!
+ \note When any pointers are invalid, always returns true.
+ */
+ bool operator!=(const GenericPointer& rhs) const { return !(*this == rhs); }
+
+ //@}
+
+ //!@name Stringify
+ //@{
+
+ //! Stringify the pointer into string representation.
+ /*!
+ \tparam OutputStream Type of output stream.
+ \param os The output stream.
+ */
+ template<typename OutputStream>
+ bool Stringify(OutputStream& os) const {
+ return Stringify<false, OutputStream>(os);
+ }
+
+ //! Stringify the pointer into URI fragment representation.
+ /*!
+ \tparam OutputStream Type of output stream.
+ \param os The output stream.
+ */
+ template<typename OutputStream>
+ bool StringifyUriFragment(OutputStream& os) const {
+ return Stringify<true, OutputStream>(os);
+ }
+
+ //@}
+
+ //!@name Create value
+ //@{
+
+ //! Create a value in a subtree.
+ /*!
+ If the value is not exist, it creates all parent values and a JSON Null value.
+ So it always succeed and return the newly created or existing value.
+
+ Remind that it may change types of parents according to tokens, so it
+ potentially removes previously stored values. For example, if a document
+ was an array, and "/foo" is used to create a value, then the document
+ will be changed to an object, and all existing array elements are lost.
+
+ \param root Root value of a DOM subtree to be resolved. It can be any value other than document root.
+ \param allocator Allocator for creating the values if the specified value or its parents are not exist.
+ \param alreadyExist If non-null, it stores whether the resolved value is already exist.
+ \return The resolved newly created (a JSON Null value), or already exists value.
+ */
+ ValueType& Create(ValueType& root, typename ValueType::AllocatorType& allocator, bool* alreadyExist = 0) const {
+ RAPIDJSON_ASSERT(IsValid());
+ ValueType* v = &root;
+ bool exist = true;
+ for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) {
+ if (v->IsArray() && t->name[0] == '-' && t->length == 1) {
+ v->PushBack(ValueType().Move(), allocator);
+ v = &((*v)[v->Size() - 1]);
+ exist = false;
+ }
+ else {
+ if (t->index == kPointerInvalidIndex) { // must be object name
+ if (!v->IsObject())
+ v->SetObject(); // Change to Object
+ }
+ else { // object name or array index
+ if (!v->IsArray() && !v->IsObject())
+ v->SetArray(); // Change to Array
+ }
+
+ if (v->IsArray()) {
+ if (t->index >= v->Size()) {
+ v->Reserve(t->index + 1, allocator);
+ while (t->index >= v->Size())
+ v->PushBack(ValueType().Move(), allocator);
+ exist = false;
+ }
+ v = &((*v)[t->index]);
+ }
+ else {
+ typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length));
+ if (m == v->MemberEnd()) {
+ v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator);
+ v = &(--v->MemberEnd())->value; // Assumes AddMember() appends at the end
+ exist = false;
+ }
+ else
+ v = &m->value;
+ }
+ }
+ }
+
+ if (alreadyExist)
+ *alreadyExist = exist;
+
+ return *v;
+ }
+
+ //! Creates a value in a document.
+ /*!
+ \param document A document to be resolved.
+ \param alreadyExist If non-null, it stores whether the resolved value is already exist.
+ \return The resolved newly created, or already exists value.
+ */
+ template <typename stackAllocator>
+ ValueType& Create(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, bool* alreadyExist = 0) const {
+ return Create(document, document.GetAllocator(), alreadyExist);
+ }
+
+ //@}
+
+ //!@name Query value
+ //@{
+
+ //! Query a value in a subtree.
+ /*!
+ \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
+ \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token.
+ \return Pointer to the value if it can be resolved. Otherwise null.
+
+ \note
+ There are only 3 situations when a value cannot be resolved:
+ 1. A value in the path is not an array nor object.
+ 2. An object value does not contain the token.
+ 3. A token is out of range of an array value.
+
+ Use unresolvedTokenIndex to retrieve the token index.
+ */
+ ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) const {
+ RAPIDJSON_ASSERT(IsValid());
+ ValueType* v = &root;
+ for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) {
+ switch (v->GetType()) {
+ case kObjectType:
+ {
+ typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length));
+ if (m == v->MemberEnd())
+ break;
+ v = &m->value;
+ }
+ continue;
+ case kArrayType:
+ if (t->index == kPointerInvalidIndex || t->index >= v->Size())
+ break;
+ v = &((*v)[t->index]);
+ continue;
+ default:
+ break;
+ }
+
+ // Error: unresolved token
+ if (unresolvedTokenIndex)
+ *unresolvedTokenIndex = static_cast<size_t>(t - tokens_);
+ return 0;
+ }
+ return v;
+ }
+
+ //! Query a const value in a const subtree.
+ /*!
+ \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
+ \return Pointer to the value if it can be resolved. Otherwise null.
+ */
+ const ValueType* Get(const ValueType& root, size_t* unresolvedTokenIndex = 0) const {
+ return Get(const_cast<ValueType&>(root), unresolvedTokenIndex);
+ }
+
+ //@}
+
+ //!@name Query a value with default
+ //@{
+
+ //! Query a value in a subtree with default value.
+ /*!
+ Similar to Get(), but if the specified value do not exists, it creates all parents and clone the default value.
+ So that this function always succeed.
+
+ \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
+ \param defaultValue Default value to be cloned if the value was not exists.
+ \param allocator Allocator for creating the values if the specified value or its parents are not exist.
+ \see Create()
+ */
+ ValueType& GetWithDefault(ValueType& root, const ValueType& defaultValue, typename ValueType::AllocatorType& allocator) const {
+ bool alreadyExist;
+ Value& v = Create(root, allocator, &alreadyExist);
+ return alreadyExist ? v : v.CopyFrom(defaultValue, allocator);
+ }
+
+ //! Query a value in a subtree with default null-terminated string.
+ ValueType& GetWithDefault(ValueType& root, const Ch* defaultValue, typename ValueType::AllocatorType& allocator) const {
+ bool alreadyExist;
+ Value& v = Create(root, allocator, &alreadyExist);
+ return alreadyExist ? v : v.SetString(defaultValue, allocator);
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Query a value in a subtree with default std::basic_string.
+ ValueType& GetWithDefault(ValueType& root, const std::basic_string<Ch>& defaultValue, typename ValueType::AllocatorType& allocator) const {
+ bool alreadyExist;
+ Value& v = Create(root, allocator, &alreadyExist);
+ return alreadyExist ? v : v.SetString(defaultValue, allocator);
+ }
+#endif
+
+ //! Query a value in a subtree with default primitive value.
+ /*!
+ \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool
+ */
+ template <typename T>
+ RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&))
+ GetWithDefault(ValueType& root, T defaultValue, typename ValueType::AllocatorType& allocator) const {
+ return GetWithDefault(root, ValueType(defaultValue).Move(), allocator);
+ }
+
+ //! Query a value in a document with default value.
+ template <typename stackAllocator>
+ ValueType& GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const ValueType& defaultValue) const {
+ return GetWithDefault(document, defaultValue, document.GetAllocator());
+ }
+
+ //! Query a value in a document with default null-terminated string.
+ template <typename stackAllocator>
+ ValueType& GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const Ch* defaultValue) const {
+ return GetWithDefault(document, defaultValue, document.GetAllocator());
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Query a value in a document with default std::basic_string.
+ template <typename stackAllocator>
+ ValueType& GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const std::basic_string<Ch>& defaultValue) const {
+ return GetWithDefault(document, defaultValue, document.GetAllocator());
+ }
+#endif
+
+ //! Query a value in a document with default primitive value.
+ /*!
+ \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool
+ */
+ template <typename T, typename stackAllocator>
+ RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&))
+ GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, T defaultValue) const {
+ return GetWithDefault(document, defaultValue, document.GetAllocator());
+ }
+
+ //@}
+
+ //!@name Set a value
+ //@{
+
+ //! Set a value in a subtree, with move semantics.
+ /*!
+ It creates all parents if they are not exist or types are different to the tokens.
+ So this function always succeeds but potentially remove existing values.
+
+ \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
+ \param value Value to be set.
+ \param allocator Allocator for creating the values if the specified value or its parents are not exist.
+ \see Create()
+ */
+ ValueType& Set(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const {
+ return Create(root, allocator) = value;
+ }
+
+ //! Set a value in a subtree, with copy semantics.
+ ValueType& Set(ValueType& root, const ValueType& value, typename ValueType::AllocatorType& allocator) const {
+ return Create(root, allocator).CopyFrom(value, allocator);
+ }
+
+ //! Set a null-terminated string in a subtree.
+ ValueType& Set(ValueType& root, const Ch* value, typename ValueType::AllocatorType& allocator) const {
+ return Create(root, allocator) = ValueType(value, allocator).Move();
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Set a std::basic_string in a subtree.
+ ValueType& Set(ValueType& root, const std::basic_string<Ch>& value, typename ValueType::AllocatorType& allocator) const {
+ return Create(root, allocator) = ValueType(value, allocator).Move();
+ }
+#endif
+
+ //! Set a primitive value in a subtree.
+ /*!
+ \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool
+ */
+ template <typename T>
+ RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&))
+ Set(ValueType& root, T value, typename ValueType::AllocatorType& allocator) const {
+ return Create(root, allocator) = ValueType(value).Move();
+ }
+
+ //! Set a value in a document, with move semantics.
+ template <typename stackAllocator>
+ ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, ValueType& value) const {
+ return Create(document) = value;
+ }
+
+ //! Set a value in a document, with copy semantics.
+ template <typename stackAllocator>
+ ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const ValueType& value) const {
+ return Create(document).CopyFrom(value, document.GetAllocator());
+ }
+
+ //! Set a null-terminated string in a document.
+ template <typename stackAllocator>
+ ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const Ch* value) const {
+ return Create(document) = ValueType(value, document.GetAllocator()).Move();
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Sets a std::basic_string in a document.
+ template <typename stackAllocator>
+ ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const std::basic_string<Ch>& value) const {
+ return Create(document) = ValueType(value, document.GetAllocator()).Move();
+ }
+#endif
+
+ //! Set a primitive value in a document.
+ /*!
+ \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool
+ */
+ template <typename T, typename stackAllocator>
+ RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&))
+ Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, T value) const {
+ return Create(document) = value;
+ }
+
+ //@}
+
+ //!@name Swap a value
+ //@{
+
+ //! Swap a value with a value in a subtree.
+ /*!
+ It creates all parents if they are not exist or types are different to the tokens.
+ So this function always succeeds but potentially remove existing values.
+
+ \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
+ \param value Value to be swapped.
+ \param allocator Allocator for creating the values if the specified value or its parents are not exist.
+ \see Create()
+ */
+ ValueType& Swap(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const {
+ return Create(root, allocator).Swap(value);
+ }
+
+ //! Swap a value with a value in a document.
+ template <typename stackAllocator>
+ ValueType& Swap(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, ValueType& value) const {
+ return Create(document).Swap(value);
+ }
+
+ //@}
+
+ //! Erase a value in a subtree.
+ /*!
+ \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
+ \return Whether the resolved value is found and erased.
+
+ \note Erasing with an empty pointer \c Pointer(""), i.e. the root, always fail and return false.
+ */
+ bool Erase(ValueType& root) const {
+ RAPIDJSON_ASSERT(IsValid());
+ if (tokenCount_ == 0) // Cannot erase the root
+ return false;
+
+ ValueType* v = &root;
+ const Token* last = tokens_ + (tokenCount_ - 1);
+ for (const Token *t = tokens_; t != last; ++t) {
+ switch (v->GetType()) {
+ case kObjectType:
+ {
+ typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length));
+ if (m == v->MemberEnd())
+ return false;
+ v = &m->value;
+ }
+ break;
+ case kArrayType:
+ if (t->index == kPointerInvalidIndex || t->index >= v->Size())
+ return false;
+ v = &((*v)[t->index]);
+ break;
+ default:
+ return false;
+ }
+ }
+
+ switch (v->GetType()) {
+ case kObjectType:
+ return v->EraseMember(GenericStringRef<Ch>(last->name, last->length));
+ case kArrayType:
+ if (last->index == kPointerInvalidIndex || last->index >= v->Size())
+ return false;
+ v->Erase(v->Begin() + last->index);
+ return true;
+ default:
+ return false;
+ }
+ }
+
+private:
+ //! Clone the content from rhs to this.
+ /*!
+ \param rhs Source pointer.
+ \param extraToken Extra tokens to be allocated.
+ \param extraNameBufferSize Extra name buffer size (in number of Ch) to be allocated.
+ \return Start of non-occupied name buffer, for storing extra names.
+ */
+ Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) {
+ if (!allocator_) // allocator is independently owned.
+ ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
+
+ size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens
+ for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t)
+ nameBufferSize += t->length;
+
+ tokenCount_ = rhs.tokenCount_ + extraToken;
+ tokens_ = static_cast<Token *>(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch)));
+ nameBuffer_ = reinterpret_cast<Ch *>(tokens_ + tokenCount_);
+ if (rhs.tokenCount_ > 0) {
+ std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token));
+ }
+ if (nameBufferSize > 0) {
+ std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch));
+ }
+
+ // Adjust pointers to name buffer
+ std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_;
+ for (Token *t = tokens_; t != tokens_ + rhs.tokenCount_; ++t)
+ t->name += diff;
+
+ return nameBuffer_ + nameBufferSize;
+ }
+
+ //! Check whether a character should be percent-encoded.
+ /*!
+ According to RFC 3986 2.3 Unreserved Characters.
+ \param c The character (code unit) to be tested.
+ */
+ bool NeedPercentEncode(Ch c) const {
+ return !((c >= '0' && c <= '9') || (c >= 'A' && c <='Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '.' || c == '_' || c =='~');
+ }
+
+ //! Parse a JSON String or its URI fragment representation into tokens.
+#ifndef __clang__ // -Wdocumentation
+ /*!
+ \param source Either a JSON Pointer string, or its URI fragment representation. Not need to be null terminated.
+ \param length Length of the source string.
+ \note Source cannot be JSON String Representation of JSON Pointer, e.g. In "/\u0000", \u0000 will not be unescaped.
+ */
+#endif
+ void Parse(const Ch* source, size_t length) {
+ RAPIDJSON_ASSERT(source != NULL);
+ RAPIDJSON_ASSERT(nameBuffer_ == 0);
+ RAPIDJSON_ASSERT(tokens_ == 0);
+
+ // Create own allocator if user did not supply.
+ if (!allocator_)
+ ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
+
+ // Count number of '/' as tokenCount
+ tokenCount_ = 0;
+ for (const Ch* s = source; s != source + length; s++)
+ if (*s == '/')
+ tokenCount_++;
+
+ Token* token = tokens_ = static_cast<Token *>(allocator_->Malloc(tokenCount_ * sizeof(Token) + length * sizeof(Ch)));
+ Ch* name = nameBuffer_ = reinterpret_cast<Ch *>(tokens_ + tokenCount_);
+ size_t i = 0;
+
+ // Detect if it is a URI fragment
+ bool uriFragment = false;
+ if (source[i] == '#') {
+ uriFragment = true;
+ i++;
+ }
+
+ if (i != length && source[i] != '/') {
+ parseErrorCode_ = kPointerParseErrorTokenMustBeginWithSolidus;
+ goto error;
+ }
+
+ while (i < length) {
+ RAPIDJSON_ASSERT(source[i] == '/');
+ i++; // consumes '/'
+
+ token->name = name;
+ bool isNumber = true;
+
+ while (i < length && source[i] != '/') {
+ Ch c = source[i];
+ if (uriFragment) {
+ // Decoding percent-encoding for URI fragment
+ if (c == '%') {
+ PercentDecodeStream is(&source[i], source + length);
+ GenericInsituStringStream<EncodingType> os(name);
+ Ch* begin = os.PutBegin();
+ if (!Transcoder<UTF8<>, EncodingType>().Validate(is, os) || !is.IsValid()) {
+ parseErrorCode_ = kPointerParseErrorInvalidPercentEncoding;
+ goto error;
+ }
+ size_t len = os.PutEnd(begin);
+ i += is.Tell() - 1;
+ if (len == 1)
+ c = *name;
+ else {
+ name += len;
+ isNumber = false;
+ i++;
+ continue;
+ }
+ }
+ else if (NeedPercentEncode(c)) {
+ parseErrorCode_ = kPointerParseErrorCharacterMustPercentEncode;
+ goto error;
+ }
+ }
+
+ i++;
+
+ // Escaping "~0" -> '~', "~1" -> '/'
+ if (c == '~') {
+ if (i < length) {
+ c = source[i];
+ if (c == '0') c = '~';
+ else if (c == '1') c = '/';
+ else {
+ parseErrorCode_ = kPointerParseErrorInvalidEscape;
+ goto error;
+ }
+ i++;
+ }
+ else {
+ parseErrorCode_ = kPointerParseErrorInvalidEscape;
+ goto error;
+ }
+ }
+
+ // First check for index: all of characters are digit
+ if (c < '0' || c > '9')
+ isNumber = false;
+
+ *name++ = c;
+ }
+ token->length = static_cast<SizeType>(name - token->name);
+ if (token->length == 0)
+ isNumber = false;
+ *name++ = '\0'; // Null terminator
+
+ // Second check for index: more than one digit cannot have leading zero
+ if (isNumber && token->length > 1 && token->name[0] == '0')
+ isNumber = false;
+
+ // String to SizeType conversion
+ SizeType n = 0;
+ if (isNumber) {
+ for (size_t j = 0; j < token->length; j++) {
+ SizeType m = n * 10 + static_cast<SizeType>(token->name[j] - '0');
+ if (m < n) { // overflow detection
+ isNumber = false;
+ break;
+ }
+ n = m;
+ }
+ }
+
+ token->index = isNumber ? n : kPointerInvalidIndex;
+ token++;
+ }
+
+ RAPIDJSON_ASSERT(name <= nameBuffer_ + length); // Should not overflow buffer
+ parseErrorCode_ = kPointerParseErrorNone;
+ return;
+
+ error:
+ Allocator::Free(tokens_);
+ nameBuffer_ = 0;
+ tokens_ = 0;
+ tokenCount_ = 0;
+ parseErrorOffset_ = i;
+ return;
+ }
+
+ //! Stringify to string or URI fragment representation.
+ /*!
+ \tparam uriFragment True for stringifying to URI fragment representation. False for string representation.
+ \tparam OutputStream type of output stream.
+ \param os The output stream.
+ */
+ template<bool uriFragment, typename OutputStream>
+ bool Stringify(OutputStream& os) const {
+ RAPIDJSON_ASSERT(IsValid());
+
+ if (uriFragment)
+ os.Put('#');
+
+ for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) {
+ os.Put('/');
+ for (size_t j = 0; j < t->length; j++) {
+ Ch c = t->name[j];
+ if (c == '~') {
+ os.Put('~');
+ os.Put('0');
+ }
+ else if (c == '/') {
+ os.Put('~');
+ os.Put('1');
+ }
+ else if (uriFragment && NeedPercentEncode(c)) {
+ // Transcode to UTF8 sequence
+ GenericStringStream<typename ValueType::EncodingType> source(&t->name[j]);
+ PercentEncodeStream<OutputStream> target(os);
+ if (!Transcoder<EncodingType, UTF8<> >().Validate(source, target))
+ return false;
+ j += source.Tell() - 1;
+ }
+ else
+ os.Put(c);
+ }
+ }
+ return true;
+ }
+
+ //! A helper stream for decoding a percent-encoded sequence into code unit.
+ /*!
+ This stream decodes %XY triplet into code unit (0-255).
+ If it encounters invalid characters, it sets output code unit as 0 and
+ mark invalid, and to be checked by IsValid().
+ */
+ class PercentDecodeStream {
+ public:
+ typedef typename ValueType::Ch Ch;
+
+ //! Constructor
+ /*!
+ \param source Start of the stream
+ \param end Past-the-end of the stream.
+ */
+ PercentDecodeStream(const Ch* source, const Ch* end) : src_(source), head_(source), end_(end), valid_(true) {}
+
+ Ch Take() {
+ if (*src_ != '%' || src_ + 3 > end_) { // %XY triplet
+ valid_ = false;
+ return 0;
+ }
+ src_++;
+ Ch c = 0;
+ for (int j = 0; j < 2; j++) {
+ c = static_cast<Ch>(c << 4);
+ Ch h = *src_;
+ if (h >= '0' && h <= '9') c = static_cast<Ch>(c + h - '0');
+ else if (h >= 'A' && h <= 'F') c = static_cast<Ch>(c + h - 'A' + 10);
+ else if (h >= 'a' && h <= 'f') c = static_cast<Ch>(c + h - 'a' + 10);
+ else {
+ valid_ = false;
+ return 0;
+ }
+ src_++;
+ }
+ return c;
+ }
+
+ size_t Tell() const { return static_cast<size_t>(src_ - head_); }
+ bool IsValid() const { return valid_; }
+
+ private:
+ const Ch* src_; //!< Current read position.
+ const Ch* head_; //!< Original head of the string.
+ const Ch* end_; //!< Past-the-end position.
+ bool valid_; //!< Whether the parsing is valid.
+ };
+
+ //! A helper stream to encode character (UTF-8 code unit) into percent-encoded sequence.
+ template <typename OutputStream>
+ class PercentEncodeStream {
+ public:
+ PercentEncodeStream(OutputStream& os) : os_(os) {}
+ void Put(char c) { // UTF-8 must be byte
+ unsigned char u = static_cast<unsigned char>(c);
+ static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+ os_.Put('%');
+ os_.Put(hexDigits[u >> 4]);
+ os_.Put(hexDigits[u & 15]);
+ }
+ private:
+ OutputStream& os_;
+ };
+
+ Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_.
+ Allocator* ownAllocator_; //!< Allocator owned by this Pointer.
+ Ch* nameBuffer_; //!< A buffer containing all names in tokens.
+ Token* tokens_; //!< A list of tokens.
+ size_t tokenCount_; //!< Number of tokens in tokens_.
+ size_t parseErrorOffset_; //!< Offset in code unit when parsing fail.
+ PointerParseErrorCode parseErrorCode_; //!< Parsing error code.
+};
+
+//! GenericPointer for Value (UTF-8, default allocator).
+typedef GenericPointer<Value> Pointer;
+
+//!@name Helper functions for GenericPointer
+//@{
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+typename T::ValueType& CreateValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, typename T::AllocatorType& a) {
+ return pointer.Create(root, a);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& CreateValueByPointer(T& root, const CharType(&source)[N], typename T::AllocatorType& a) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).Create(root, a);
+}
+
+// No allocator parameter
+
+template <typename DocumentType>
+typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer) {
+ return pointer.Create(document);
+}
+
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const CharType(&source)[N]) {
+ return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Create(document);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+typename T::ValueType* GetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, size_t* unresolvedTokenIndex = 0) {
+ return pointer.Get(root, unresolvedTokenIndex);
+}
+
+template <typename T>
+const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer<typename T::ValueType>& pointer, size_t* unresolvedTokenIndex = 0) {
+ return pointer.Get(root, unresolvedTokenIndex);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).Get(root, unresolvedTokenIndex);
+}
+
+template <typename T, typename CharType, size_t N>
+const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).Get(root, unresolvedTokenIndex);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer<typename T::ValueType>& pointer, const typename T::ValueType& defaultValue, typename T::AllocatorType& a) {
+ return pointer.GetWithDefault(root, defaultValue, a);
+}
+
+template <typename T>
+typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer<typename T::ValueType>& pointer, const typename T::Ch* defaultValue, typename T::AllocatorType& a) {
+ return pointer.GetWithDefault(root, defaultValue, a);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename T>
+typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer<typename T::ValueType>& pointer, const std::basic_string<typename T::Ch>& defaultValue, typename T::AllocatorType& a) {
+ return pointer.GetWithDefault(root, defaultValue, a);
+}
+#endif
+
+template <typename T, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename T::ValueType&))
+GetValueByPointerWithDefault(T& root, const GenericPointer<typename T::ValueType>& pointer, T2 defaultValue, typename T::AllocatorType& a) {
+ return pointer.GetWithDefault(root, defaultValue, a);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::ValueType& defaultValue, typename T::AllocatorType& a) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).GetWithDefault(root, defaultValue, a);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::Ch* defaultValue, typename T::AllocatorType& a) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).GetWithDefault(root, defaultValue, a);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const std::basic_string<typename T::Ch>& defaultValue, typename T::AllocatorType& a) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).GetWithDefault(root, defaultValue, a);
+}
+#endif
+
+template <typename T, typename CharType, size_t N, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename T::ValueType&))
+GetValueByPointerWithDefault(T& root, const CharType(&source)[N], T2 defaultValue, typename T::AllocatorType& a) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).GetWithDefault(root, defaultValue, a);
+}
+
+// No allocator parameter
+
+template <typename DocumentType>
+typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const typename DocumentType::ValueType& defaultValue) {
+ return pointer.GetWithDefault(document, defaultValue);
+}
+
+template <typename DocumentType>
+typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const typename DocumentType::Ch* defaultValue) {
+ return pointer.GetWithDefault(document, defaultValue);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename DocumentType>
+typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const std::basic_string<typename DocumentType::Ch>& defaultValue) {
+ return pointer.GetWithDefault(document, defaultValue);
+}
+#endif
+
+template <typename DocumentType, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename DocumentType::ValueType&))
+GetValueByPointerWithDefault(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, T2 defaultValue) {
+ return pointer.GetWithDefault(document, defaultValue);
+}
+
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& defaultValue) {
+ return GenericPointer<typename DocumentType::ValueType>(source, N - 1).GetWithDefault(document, defaultValue);
+}
+
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* defaultValue) {
+ return GenericPointer<typename DocumentType::ValueType>(source, N - 1).GetWithDefault(document, defaultValue);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const std::basic_string<typename DocumentType::Ch>& defaultValue) {
+ return GenericPointer<typename DocumentType::ValueType>(source, N - 1).GetWithDefault(document, defaultValue);
+}
+#endif
+
+template <typename DocumentType, typename CharType, size_t N, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename DocumentType::ValueType&))
+GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], T2 defaultValue) {
+ return GenericPointer<typename DocumentType::ValueType>(source, N - 1).GetWithDefault(document, defaultValue);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+typename T::ValueType& SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, typename T::ValueType& value, typename T::AllocatorType& a) {
+ return pointer.Set(root, value, a);
+}
+
+template <typename T>
+typename T::ValueType& SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, const typename T::ValueType& value, typename T::AllocatorType& a) {
+ return pointer.Set(root, value, a);
+}
+
+template <typename T>
+typename T::ValueType& SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, const typename T::Ch* value, typename T::AllocatorType& a) {
+ return pointer.Set(root, value, a);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename T>
+typename T::ValueType& SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, const std::basic_string<typename T::Ch>& value, typename T::AllocatorType& a) {
+ return pointer.Set(root, value, a);
+}
+#endif
+
+template <typename T, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename T::ValueType&))
+SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, T2 value, typename T::AllocatorType& a) {
+ return pointer.Set(root, value, a);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::ValueType& value, typename T::AllocatorType& a) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::Ch* value, typename T::AllocatorType& a) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const std::basic_string<typename T::Ch>& value, typename T::AllocatorType& a) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a);
+}
+#endif
+
+template <typename T, typename CharType, size_t N, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename T::ValueType&))
+SetValueByPointer(T& root, const CharType(&source)[N], T2 value, typename T::AllocatorType& a) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a);
+}
+
+// No allocator parameter
+
+template <typename DocumentType>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, typename DocumentType::ValueType& value) {
+ return pointer.Set(document, value);
+}
+
+template <typename DocumentType>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const typename DocumentType::ValueType& value) {
+ return pointer.Set(document, value);
+}
+
+template <typename DocumentType>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const typename DocumentType::Ch* value) {
+ return pointer.Set(document, value);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename DocumentType>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const std::basic_string<typename DocumentType::Ch>& value) {
+ return pointer.Set(document, value);
+}
+#endif
+
+template <typename DocumentType, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename DocumentType::ValueType&))
+SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, T2 value) {
+ return pointer.Set(document, value);
+}
+
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) {
+ return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value);
+}
+
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& value) {
+ return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value);
+}
+
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* value) {
+ return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const std::basic_string<typename DocumentType::Ch>& value) {
+ return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value);
+}
+#endif
+
+template <typename DocumentType, typename CharType, size_t N, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename DocumentType::ValueType&))
+SetValueByPointer(DocumentType& document, const CharType(&source)[N], T2 value) {
+ return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+typename T::ValueType& SwapValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, typename T::ValueType& value, typename T::AllocatorType& a) {
+ return pointer.Swap(root, value, a);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& SwapValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).Swap(root, value, a);
+}
+
+template <typename DocumentType>
+typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, typename DocumentType::ValueType& value) {
+ return pointer.Swap(document, value);
+}
+
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) {
+ return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Swap(document, value);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+bool EraseValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer) {
+ return pointer.Erase(root);
+}
+
+template <typename T, typename CharType, size_t N>
+bool EraseValueByPointer(T& root, const CharType(&source)[N]) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).Erase(root);
+}
+
+//@}
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_POINTER_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/prettywriter.h b/ext/librethinkdbxx/src/rapidjson/prettywriter.h
new file mode 100644
index 00000000..75dc474f
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/prettywriter.h
@@ -0,0 +1,249 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_PRETTYWRITER_H_
+#define RAPIDJSON_PRETTYWRITER_H_
+
+#include "writer.h"
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Combination of PrettyWriter format flags.
+/*! \see PrettyWriter::SetFormatOptions
+ */
+enum PrettyFormatOptions {
+ kFormatDefault = 0, //!< Default pretty formatting.
+ kFormatSingleLineArray = 1 //!< Format arrays on a single line.
+};
+
+//! Writer with indentation and spacing.
+/*!
+ \tparam OutputStream Type of ouptut os.
+ \tparam SourceEncoding Encoding of source string.
+ \tparam TargetEncoding Encoding of output stream.
+ \tparam StackAllocator Type of allocator for allocating memory of stack.
+*/
+template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags>
+class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator, writeFlags> {
+public:
+ typedef Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator> Base;
+ typedef typename Base::Ch Ch;
+
+ //! Constructor
+ /*! \param os Output stream.
+ \param allocator User supplied allocator. If it is null, it will create a private one.
+ \param levelDepth Initial capacity of stack.
+ */
+ explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
+ Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {}
+
+
+ explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
+ Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {}
+
+ //! Set custom indentation.
+ /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r').
+ \param indentCharCount Number of indent characters for each indentation level.
+ \note The default indentation is 4 spaces.
+ */
+ PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) {
+ RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r');
+ indentChar_ = indentChar;
+ indentCharCount_ = indentCharCount;
+ return *this;
+ }
+
+ //! Set pretty writer formatting options.
+ /*! \param options Formatting options.
+ */
+ PrettyWriter& SetFormatOptions(PrettyFormatOptions options) {
+ formatOptions_ = options;
+ return *this;
+ }
+
+ /*! @name Implementation of Handler
+ \see Handler
+ */
+ //@{
+
+ bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); }
+ bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); }
+ bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); }
+ bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); }
+ bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); }
+ bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); }
+ bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); }
+
+ bool RawNumber(const Ch* str, SizeType length, bool copy = false) {
+ (void)copy;
+ PrettyPrefix(kNumberType);
+ return Base::WriteString(str, length);
+ }
+
+ bool String(const Ch* str, SizeType length, bool copy = false) {
+ (void)copy;
+ PrettyPrefix(kStringType);
+ return Base::WriteString(str, length);
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ bool String(const std::basic_string<Ch>& str) {
+ return String(str.data(), SizeType(str.size()));
+ }
+#endif
+
+ bool StartObject() {
+ PrettyPrefix(kObjectType);
+ new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(false);
+ return Base::WriteStartObject();
+ }
+
+ bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); }
+
+ bool EndObject(SizeType memberCount = 0) {
+ (void)memberCount;
+ RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
+ RAPIDJSON_ASSERT(!Base::level_stack_.template Top<typename Base::Level>()->inArray);
+ bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
+
+ if (!empty) {
+ Base::os_->Put('\n');
+ WriteIndent();
+ }
+ bool ret = Base::WriteEndObject();
+ (void)ret;
+ RAPIDJSON_ASSERT(ret == true);
+ if (Base::level_stack_.Empty()) // end of json text
+ Base::os_->Flush();
+ return true;
+ }
+
+ bool StartArray() {
+ PrettyPrefix(kArrayType);
+ new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(true);
+ return Base::WriteStartArray();
+ }
+
+ bool EndArray(SizeType memberCount = 0) {
+ (void)memberCount;
+ RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
+ RAPIDJSON_ASSERT(Base::level_stack_.template Top<typename Base::Level>()->inArray);
+ bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
+
+ if (!empty && !(formatOptions_ & kFormatSingleLineArray)) {
+ Base::os_->Put('\n');
+ WriteIndent();
+ }
+ bool ret = Base::WriteEndArray();
+ (void)ret;
+ RAPIDJSON_ASSERT(ret == true);
+ if (Base::level_stack_.Empty()) // end of json text
+ Base::os_->Flush();
+ return true;
+ }
+
+ //@}
+
+ /*! @name Convenience extensions */
+ //@{
+
+ //! Simpler but slower overload.
+ bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
+ bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); }
+
+ //@}
+
+ //! Write a raw JSON value.
+ /*!
+ For user to write a stringified JSON as a value.
+
+ \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range.
+ \param length Length of the json.
+ \param type Type of the root of json.
+ \note When using PrettyWriter::RawValue(), the result json may not be indented correctly.
+ */
+ bool RawValue(const Ch* json, size_t length, Type type) { PrettyPrefix(type); return Base::WriteRawValue(json, length); }
+
+protected:
+ void PrettyPrefix(Type type) {
+ (void)type;
+ if (Base::level_stack_.GetSize() != 0) { // this value is not at root
+ typename Base::Level* level = Base::level_stack_.template Top<typename Base::Level>();
+
+ if (level->inArray) {
+ if (level->valueCount > 0) {
+ Base::os_->Put(','); // add comma if it is not the first element in array
+ if (formatOptions_ & kFormatSingleLineArray)
+ Base::os_->Put(' ');
+ }
+
+ if (!(formatOptions_ & kFormatSingleLineArray)) {
+ Base::os_->Put('\n');
+ WriteIndent();
+ }
+ }
+ else { // in object
+ if (level->valueCount > 0) {
+ if (level->valueCount % 2 == 0) {
+ Base::os_->Put(',');
+ Base::os_->Put('\n');
+ }
+ else {
+ Base::os_->Put(':');
+ Base::os_->Put(' ');
+ }
+ }
+ else
+ Base::os_->Put('\n');
+
+ if (level->valueCount % 2 == 0)
+ WriteIndent();
+ }
+ if (!level->inArray && level->valueCount % 2 == 0)
+ RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name
+ level->valueCount++;
+ }
+ else {
+ RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root.
+ Base::hasRoot_ = true;
+ }
+ }
+
+ void WriteIndent() {
+ size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_;
+ PutN(*Base::os_, static_cast<typename TargetEncoding::Ch>(indentChar_), count);
+ }
+
+ Ch indentChar_;
+ unsigned indentCharCount_;
+ PrettyFormatOptions formatOptions_;
+
+private:
+ // Prohibit copy constructor & assignment operator.
+ PrettyWriter(const PrettyWriter&);
+ PrettyWriter& operator=(const PrettyWriter&);
+};
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_RAPIDJSON_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/rapidjson.h b/ext/librethinkdbxx/src/rapidjson/rapidjson.h
new file mode 100644
index 00000000..d666f202
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/rapidjson.h
@@ -0,0 +1,615 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_RAPIDJSON_H_
+#define RAPIDJSON_RAPIDJSON_H_
+
+/*!\file rapidjson.h
+ \brief common definitions and configuration
+
+ \see RAPIDJSON_CONFIG
+ */
+
+/*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration
+ \brief Configuration macros for library features
+
+ Some RapidJSON features are configurable to adapt the library to a wide
+ variety of platforms, environments and usage scenarios. Most of the
+ features can be configured in terms of overriden or predefined
+ preprocessor macros at compile-time.
+
+ Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs.
+
+ \note These macros should be given on the compiler command-line
+ (where applicable) to avoid inconsistent values when compiling
+ different translation units of a single application.
+ */
+
+#include <cstdlib> // malloc(), realloc(), free(), size_t
+#include <cstring> // memset(), memcpy(), memmove(), memcmp()
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_VERSION_STRING
+//
+// ALWAYS synchronize the following 3 macros with corresponding variables in /CMakeLists.txt.
+//
+
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+// token stringification
+#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x)
+#define RAPIDJSON_DO_STRINGIFY(x) #x
+//!@endcond
+
+/*! \def RAPIDJSON_MAJOR_VERSION
+ \ingroup RAPIDJSON_CONFIG
+ \brief Major version of RapidJSON in integer.
+*/
+/*! \def RAPIDJSON_MINOR_VERSION
+ \ingroup RAPIDJSON_CONFIG
+ \brief Minor version of RapidJSON in integer.
+*/
+/*! \def RAPIDJSON_PATCH_VERSION
+ \ingroup RAPIDJSON_CONFIG
+ \brief Patch version of RapidJSON in integer.
+*/
+/*! \def RAPIDJSON_VERSION_STRING
+ \ingroup RAPIDJSON_CONFIG
+ \brief Version of RapidJSON in "<major>.<minor>.<patch>" string format.
+*/
+#define RAPIDJSON_MAJOR_VERSION 1
+#define RAPIDJSON_MINOR_VERSION 0
+#define RAPIDJSON_PATCH_VERSION 2
+#define RAPIDJSON_VERSION_STRING \
+ RAPIDJSON_STRINGIFY(RAPIDJSON_MAJOR_VERSION.RAPIDJSON_MINOR_VERSION.RAPIDJSON_PATCH_VERSION)
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_NAMESPACE_(BEGIN|END)
+/*! \def RAPIDJSON_NAMESPACE
+ \ingroup RAPIDJSON_CONFIG
+ \brief provide custom rapidjson namespace
+
+ In order to avoid symbol clashes and/or "One Definition Rule" errors
+ between multiple inclusions of (different versions of) RapidJSON in
+ a single binary, users can customize the name of the main RapidJSON
+ namespace.
+
+ In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE
+ to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple
+ levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref
+ RAPIDJSON_NAMESPACE_END need to be defined as well:
+
+ \code
+ // in some .cpp file
+ #define RAPIDJSON_NAMESPACE my::rapidjson
+ #define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson {
+ #define RAPIDJSON_NAMESPACE_END } }
+ #include "rapidjson/..."
+ \endcode
+
+ \see rapidjson
+ */
+/*! \def RAPIDJSON_NAMESPACE_BEGIN
+ \ingroup RAPIDJSON_CONFIG
+ \brief provide custom rapidjson namespace (opening expression)
+ \see RAPIDJSON_NAMESPACE
+*/
+/*! \def RAPIDJSON_NAMESPACE_END
+ \ingroup RAPIDJSON_CONFIG
+ \brief provide custom rapidjson namespace (closing expression)
+ \see RAPIDJSON_NAMESPACE
+*/
+#ifndef RAPIDJSON_NAMESPACE
+#define RAPIDJSON_NAMESPACE rapidjson
+#endif
+#ifndef RAPIDJSON_NAMESPACE_BEGIN
+#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE {
+#endif
+#ifndef RAPIDJSON_NAMESPACE_END
+#define RAPIDJSON_NAMESPACE_END }
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_HAS_STDSTRING
+
+#ifndef RAPIDJSON_HAS_STDSTRING
+#ifdef RAPIDJSON_DOXYGEN_RUNNING
+#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation
+#else
+#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default
+#endif
+/*! \def RAPIDJSON_HAS_STDSTRING
+ \ingroup RAPIDJSON_CONFIG
+ \brief Enable RapidJSON support for \c std::string
+
+ By defining this preprocessor symbol to \c 1, several convenience functions for using
+ \ref rapidjson::GenericValue with \c std::string are enabled, especially
+ for construction and comparison.
+
+ \hideinitializer
+*/
+#endif // !defined(RAPIDJSON_HAS_STDSTRING)
+
+#if RAPIDJSON_HAS_STDSTRING
+#include <string>
+#endif // RAPIDJSON_HAS_STDSTRING
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_NO_INT64DEFINE
+
+/*! \def RAPIDJSON_NO_INT64DEFINE
+ \ingroup RAPIDJSON_CONFIG
+ \brief Use external 64-bit integer types.
+
+ RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types
+ to be available at global scope.
+
+ If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to
+ prevent RapidJSON from defining its own types.
+*/
+#ifndef RAPIDJSON_NO_INT64DEFINE
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013
+#include "msinttypes/stdint.h"
+#include "msinttypes/inttypes.h"
+#else
+// Other compilers should have this.
+#include <stdint.h>
+#include <inttypes.h>
+#endif
+//!@endcond
+#ifdef RAPIDJSON_DOXYGEN_RUNNING
+#define RAPIDJSON_NO_INT64DEFINE
+#endif
+#endif // RAPIDJSON_NO_INT64TYPEDEF
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_FORCEINLINE
+
+#ifndef RAPIDJSON_FORCEINLINE
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+#if defined(_MSC_VER) && defined(NDEBUG)
+#define RAPIDJSON_FORCEINLINE __forceinline
+#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG)
+#define RAPIDJSON_FORCEINLINE __attribute__((always_inline))
+#else
+#define RAPIDJSON_FORCEINLINE
+#endif
+//!@endcond
+#endif // RAPIDJSON_FORCEINLINE
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_ENDIAN
+#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine
+#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine
+
+//! Endianness of the machine.
+/*!
+ \def RAPIDJSON_ENDIAN
+ \ingroup RAPIDJSON_CONFIG
+
+ GCC 4.6 provided macro for detecting endianness of the target machine. But other
+ compilers may not have this. User can define RAPIDJSON_ENDIAN to either
+ \ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN.
+
+ Default detection implemented with reference to
+ \li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html
+ \li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp
+*/
+#ifndef RAPIDJSON_ENDIAN
+// Detect with GCC 4.6's macro
+# ifdef __BYTE_ORDER__
+# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
+# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
+# else
+# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN.
+# endif // __BYTE_ORDER__
+// Detect with GLIBC's endian.h
+# elif defined(__GLIBC__)
+# include <endian.h>
+# if (__BYTE_ORDER == __LITTLE_ENDIAN)
+# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
+# elif (__BYTE_ORDER == __BIG_ENDIAN)
+# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
+# else
+# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN.
+# endif // __GLIBC__
+// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro
+# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)
+# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
+# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)
+# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
+// Detect with architecture macros
+# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__)
+# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
+# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__)
+# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
+# elif defined(_MSC_VER) && defined(_M_ARM)
+# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
+# elif defined(RAPIDJSON_DOXYGEN_RUNNING)
+# define RAPIDJSON_ENDIAN
+# else
+# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN.
+# endif
+#endif // RAPIDJSON_ENDIAN
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_64BIT
+
+//! Whether using 64-bit architecture
+#ifndef RAPIDJSON_64BIT
+#if defined(__LP64__) || defined(_WIN64) || defined(__EMSCRIPTEN__)
+#define RAPIDJSON_64BIT 1
+#else
+#define RAPIDJSON_64BIT 0
+#endif
+#endif // RAPIDJSON_64BIT
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_ALIGN
+
+//! Data alignment of the machine.
+/*! \ingroup RAPIDJSON_CONFIG
+ \param x pointer to align
+
+ Some machines require strict data alignment. Currently the default uses 4 bytes
+ alignment on 32-bit platforms and 8 bytes alignment for 64-bit platforms.
+ User can customize by defining the RAPIDJSON_ALIGN function macro.
+*/
+#ifndef RAPIDJSON_ALIGN
+#if RAPIDJSON_64BIT == 1
+#define RAPIDJSON_ALIGN(x) (((x) + static_cast<uint64_t>(7u)) & ~static_cast<uint64_t>(7u))
+#else
+#define RAPIDJSON_ALIGN(x) (((x) + 3u) & ~3u)
+#endif
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_UINT64_C2
+
+//! Construct a 64-bit literal by a pair of 32-bit integer.
+/*!
+ 64-bit literal with or without ULL suffix is prone to compiler warnings.
+ UINT64_C() is C macro which cause compilation problems.
+ Use this macro to define 64-bit constants by a pair of 32-bit integer.
+*/
+#ifndef RAPIDJSON_UINT64_C2
+#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast<uint64_t>(high32) << 32) | static_cast<uint64_t>(low32))
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_48BITPOINTER_OPTIMIZATION
+
+//! Use only lower 48-bit address for some pointers.
+/*!
+ \ingroup RAPIDJSON_CONFIG
+
+ This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address.
+ The higher 16-bit can be used for storing other data.
+ \c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture.
+*/
+#ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION
+#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)
+#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1
+#else
+#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0
+#endif
+#endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION
+
+#if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1
+#if RAPIDJSON_64BIT != 1
+#error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1
+#endif
+#define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast<type *>((reinterpret_cast<uintptr_t>(p) & static_cast<uintptr_t>(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast<uintptr_t>(reinterpret_cast<const void*>(x))))
+#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast<type *>(reinterpret_cast<uintptr_t>(p) & static_cast<uintptr_t>(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF))))
+#else
+#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x))
+#define RAPIDJSON_GETPOINTER(type, p) (p)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD
+
+/*! \def RAPIDJSON_SIMD
+ \ingroup RAPIDJSON_CONFIG
+ \brief Enable SSE2/SSE4.2 optimization.
+
+ RapidJSON supports optimized implementations for some parsing operations
+ based on the SSE2 or SSE4.2 SIMD extensions on modern Intel-compatible
+ processors.
+
+ To enable these optimizations, two different symbols can be defined;
+ \code
+ // Enable SSE2 optimization.
+ #define RAPIDJSON_SSE2
+
+ // Enable SSE4.2 optimization.
+ #define RAPIDJSON_SSE42
+ \endcode
+
+ \c RAPIDJSON_SSE42 takes precedence, if both are defined.
+
+ If any of these symbols is defined, RapidJSON defines the macro
+ \c RAPIDJSON_SIMD to indicate the availability of the optimized code.
+*/
+#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \
+ || defined(RAPIDJSON_DOXYGEN_RUNNING)
+#define RAPIDJSON_SIMD
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_NO_SIZETYPEDEFINE
+
+#ifndef RAPIDJSON_NO_SIZETYPEDEFINE
+/*! \def RAPIDJSON_NO_SIZETYPEDEFINE
+ \ingroup RAPIDJSON_CONFIG
+ \brief User-provided \c SizeType definition.
+
+ In order to avoid using 32-bit size types for indexing strings and arrays,
+ define this preprocessor symbol and provide the type rapidjson::SizeType
+ before including RapidJSON:
+ \code
+ #define RAPIDJSON_NO_SIZETYPEDEFINE
+ namespace rapidjson { typedef ::std::size_t SizeType; }
+ #include "rapidjson/..."
+ \endcode
+
+ \see rapidjson::SizeType
+*/
+#ifdef RAPIDJSON_DOXYGEN_RUNNING
+#define RAPIDJSON_NO_SIZETYPEDEFINE
+#endif
+RAPIDJSON_NAMESPACE_BEGIN
+//! Size type (for string lengths, array sizes, etc.)
+/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms,
+ instead of using \c size_t. Users may override the SizeType by defining
+ \ref RAPIDJSON_NO_SIZETYPEDEFINE.
+*/
+typedef unsigned SizeType;
+RAPIDJSON_NAMESPACE_END
+#endif
+
+// always import std::size_t to rapidjson namespace
+RAPIDJSON_NAMESPACE_BEGIN
+using std::size_t;
+RAPIDJSON_NAMESPACE_END
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_ASSERT
+
+//! Assertion.
+/*! \ingroup RAPIDJSON_CONFIG
+ By default, rapidjson uses C \c assert() for internal assertions.
+ User can override it by defining RAPIDJSON_ASSERT(x) macro.
+
+ \note Parsing errors are handled and can be customized by the
+ \ref RAPIDJSON_ERRORS APIs.
+*/
+#ifndef RAPIDJSON_ASSERT
+#include <cassert>
+#define RAPIDJSON_ASSERT(x) assert(x)
+#endif // RAPIDJSON_ASSERT
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_STATIC_ASSERT
+
+// Adopt from boost
+#ifndef RAPIDJSON_STATIC_ASSERT
+#ifndef __clang__
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+#endif
+RAPIDJSON_NAMESPACE_BEGIN
+template <bool x> struct STATIC_ASSERTION_FAILURE;
+template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; };
+template<int x> struct StaticAssertTest {};
+RAPIDJSON_NAMESPACE_END
+
+#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y)
+#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y)
+#define RAPIDJSON_DO_JOIN2(X, Y) X##Y
+
+#if defined(__GNUC__)
+#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused))
+#else
+#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE
+#endif
+#ifndef __clang__
+//!@endcond
+#endif
+
+/*! \def RAPIDJSON_STATIC_ASSERT
+ \brief (Internal) macro to check for conditions at compile-time
+ \param x compile-time condition
+ \hideinitializer
+ */
+#define RAPIDJSON_STATIC_ASSERT(x) \
+ typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \
+ sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE<bool(x) >)> \
+ RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY
+
+//! Compiler branching hint for expression with high probability to be true.
+/*!
+ \ingroup RAPIDJSON_CONFIG
+ \param x Boolean expression likely to be true.
+*/
+#ifndef RAPIDJSON_LIKELY
+#if defined(__GNUC__) || defined(__clang__)
+#define RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1)
+#else
+#define RAPIDJSON_LIKELY(x) (x)
+#endif
+#endif
+
+//! Compiler branching hint for expression with low probability to be true.
+/*!
+ \ingroup RAPIDJSON_CONFIG
+ \param x Boolean expression unlikely to be true.
+*/
+#ifndef RAPIDJSON_UNLIKELY
+#if defined(__GNUC__) || defined(__clang__)
+#define RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+#define RAPIDJSON_UNLIKELY(x) (x)
+#endif
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers
+
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+
+#define RAPIDJSON_MULTILINEMACRO_BEGIN do {
+#define RAPIDJSON_MULTILINEMACRO_END \
+} while((void)0, 0)
+
+// adopted from Boost
+#define RAPIDJSON_VERSION_CODE(x,y,z) \
+ (((x)*100000) + ((y)*100) + (z))
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF
+
+#if defined(__GNUC__)
+#define RAPIDJSON_GNUC \
+ RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__)
+#endif
+
+#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0))
+
+#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x))
+#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x)
+#define RAPIDJSON_DIAG_OFF(x) \
+ RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x)))
+
+// push/pop support in Clang and GCC>=4.6
+#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0))
+#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push)
+#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop)
+#else // GCC >= 4.2, < 4.6
+#define RAPIDJSON_DIAG_PUSH /* ignored */
+#define RAPIDJSON_DIAG_POP /* ignored */
+#endif
+
+#elif defined(_MSC_VER)
+
+// pragma (MSVC specific)
+#define RAPIDJSON_PRAGMA(x) __pragma(x)
+#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x))
+
+#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x)
+#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push)
+#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop)
+
+#else
+
+#define RAPIDJSON_DIAG_OFF(x) /* ignored */
+#define RAPIDJSON_DIAG_PUSH /* ignored */
+#define RAPIDJSON_DIAG_POP /* ignored */
+
+#endif // RAPIDJSON_DIAG_*
+
+///////////////////////////////////////////////////////////////////////////////
+// C++11 features
+
+#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS
+#if defined(__clang__)
+#if __has_feature(cxx_rvalue_references) && \
+ (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306)
+#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
+#else
+#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0
+#endif
+#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1600)
+
+#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
+#else
+#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0
+#endif
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT
+#if defined(__clang__)
+#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept)
+#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__))
+// (defined(_MSC_VER) && _MSC_VER >= ????) // not yet supported
+#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1
+#else
+#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0
+#endif
+#endif
+#if RAPIDJSON_HAS_CXX11_NOEXCEPT
+#define RAPIDJSON_NOEXCEPT noexcept
+#else
+#define RAPIDJSON_NOEXCEPT /* noexcept */
+#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT
+
+// no automatic detection, yet
+#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS
+#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0
+#endif
+
+#ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR
+#if defined(__clang__)
+#define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for)
+#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1700)
+#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1
+#else
+#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0
+#endif
+#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR
+
+//!@endcond
+
+///////////////////////////////////////////////////////////////////////////////
+// new/delete
+
+#ifndef RAPIDJSON_NEW
+///! customization point for global \c new
+#define RAPIDJSON_NEW(x) new x
+#endif
+#ifndef RAPIDJSON_DELETE
+///! customization point for global \c delete
+#define RAPIDJSON_DELETE(x) delete x
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Type
+
+/*! \namespace rapidjson
+ \brief main RapidJSON namespace
+ \see RAPIDJSON_NAMESPACE
+*/
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Type of JSON value
+enum Type {
+ kNullType = 0, //!< null
+ kFalseType = 1, //!< false
+ kTrueType = 2, //!< true
+ kObjectType = 3, //!< object
+ kArrayType = 4, //!< array
+ kStringType = 5, //!< string
+ kNumberType = 6 //!< number
+};
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_RAPIDJSON_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/reader.h b/ext/librethinkdbxx/src/rapidjson/reader.h
new file mode 100644
index 00000000..19f8849b
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/reader.h
@@ -0,0 +1,1879 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_READER_H_
+#define RAPIDJSON_READER_H_
+
+/*! \file reader.h */
+
+#include "allocators.h"
+#include "stream.h"
+#include "encodedstream.h"
+#include "internal/meta.h"
+#include "internal/stack.h"
+#include "internal/strtod.h"
+#include <limits>
+
+#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER)
+#include <intrin.h>
+#pragma intrinsic(_BitScanForward)
+#endif
+#ifdef RAPIDJSON_SSE42
+#include <nmmintrin.h>
+#elif defined(RAPIDJSON_SSE2)
+#include <emmintrin.h>
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
+RAPIDJSON_DIAG_OFF(4702) // unreachable code
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(old-style-cast)
+RAPIDJSON_DIAG_OFF(padded)
+RAPIDJSON_DIAG_OFF(switch-enum)
+#endif
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+#define RAPIDJSON_NOTHING /* deliberately empty */
+#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN
+#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \
+ RAPIDJSON_MULTILINEMACRO_BEGIN \
+ if (RAPIDJSON_UNLIKELY(HasParseError())) { return value; } \
+ RAPIDJSON_MULTILINEMACRO_END
+#endif
+#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING)
+//!@endcond
+
+/*! \def RAPIDJSON_PARSE_ERROR_NORETURN
+ \ingroup RAPIDJSON_ERRORS
+ \brief Macro to indicate a parse error.
+ \param parseErrorCode \ref rapidjson::ParseErrorCode of the error
+ \param offset position of the error in JSON input (\c size_t)
+
+ This macros can be used as a customization point for the internal
+ error handling mechanism of RapidJSON.
+
+ A common usage model is to throw an exception instead of requiring the
+ caller to explicitly check the \ref rapidjson::GenericReader::Parse's
+ return value:
+
+ \code
+ #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode,offset) \
+ throw ParseException(parseErrorCode, #parseErrorCode, offset)
+
+ #include <stdexcept> // std::runtime_error
+ #include "rapidjson/error/error.h" // rapidjson::ParseResult
+
+ struct ParseException : std::runtime_error, rapidjson::ParseResult {
+ ParseException(rapidjson::ParseErrorCode code, const char* msg, size_t offset)
+ : std::runtime_error(msg), ParseResult(code, offset) {}
+ };
+
+ #include "rapidjson/reader.h"
+ \endcode
+
+ \see RAPIDJSON_PARSE_ERROR, rapidjson::GenericReader::Parse
+ */
+#ifndef RAPIDJSON_PARSE_ERROR_NORETURN
+#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \
+ RAPIDJSON_MULTILINEMACRO_BEGIN \
+ RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \
+ SetParseError(parseErrorCode, offset); \
+ RAPIDJSON_MULTILINEMACRO_END
+#endif
+
+/*! \def RAPIDJSON_PARSE_ERROR
+ \ingroup RAPIDJSON_ERRORS
+ \brief (Internal) macro to indicate and handle a parse error.
+ \param parseErrorCode \ref rapidjson::ParseErrorCode of the error
+ \param offset position of the error in JSON input (\c size_t)
+
+ Invokes RAPIDJSON_PARSE_ERROR_NORETURN and stops the parsing.
+
+ \see RAPIDJSON_PARSE_ERROR_NORETURN
+ \hideinitializer
+ */
+#ifndef RAPIDJSON_PARSE_ERROR
+#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \
+ RAPIDJSON_MULTILINEMACRO_BEGIN \
+ RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \
+ RAPIDJSON_MULTILINEMACRO_END
+#endif
+
+#include "error/error.h" // ParseErrorCode, ParseResult
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// ParseFlag
+
+/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS
+ \ingroup RAPIDJSON_CONFIG
+ \brief User-defined kParseDefaultFlags definition.
+
+ User can define this as any \c ParseFlag combinations.
+*/
+#ifndef RAPIDJSON_PARSE_DEFAULT_FLAGS
+#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseNoFlags
+#endif
+
+//! Combination of parseFlags
+/*! \see Reader::Parse, Document::Parse, Document::ParseInsitu, Document::ParseStream
+ */
+enum ParseFlag {
+ kParseNoFlags = 0, //!< No flags are set.
+ kParseInsituFlag = 1, //!< In-situ(destructive) parsing.
+ kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings.
+ kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing.
+ kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error.
+ kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower).
+ kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments.
+ kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings.
+ kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays.
+ kParseNanAndInfFlag = 256, //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles.
+ kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Handler
+
+/*! \class rapidjson::Handler
+ \brief Concept for receiving events from GenericReader upon parsing.
+ The functions return true if no error occurs. If they return false,
+ the event publisher should terminate the process.
+\code
+concept Handler {
+ typename Ch;
+
+ bool Null();
+ bool Bool(bool b);
+ bool Int(int i);
+ bool Uint(unsigned i);
+ bool Int64(int64_t i);
+ bool Uint64(uint64_t i);
+ bool Double(double d);
+ /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length)
+ bool RawNumber(const Ch* str, SizeType length, bool copy);
+ bool String(const Ch* str, SizeType length, bool copy);
+ bool StartObject();
+ bool Key(const Ch* str, SizeType length, bool copy);
+ bool EndObject(SizeType memberCount);
+ bool StartArray();
+ bool EndArray(SizeType elementCount);
+};
+\endcode
+*/
+///////////////////////////////////////////////////////////////////////////////
+// BaseReaderHandler
+
+//! Default implementation of Handler.
+/*! This can be used as base class of any reader handler.
+ \note implements Handler concept
+*/
+template<typename Encoding = UTF8<>, typename Derived = void>
+struct BaseReaderHandler {
+ typedef typename Encoding::Ch Ch;
+
+ typedef typename internal::SelectIf<internal::IsSame<Derived, void>, BaseReaderHandler, Derived>::Type Override;
+
+ bool Default() { return true; }
+ bool Null() { return static_cast<Override&>(*this).Default(); }
+ bool Bool(bool) { return static_cast<Override&>(*this).Default(); }
+ bool Int(int) { return static_cast<Override&>(*this).Default(); }
+ bool Uint(unsigned) { return static_cast<Override&>(*this).Default(); }
+ bool Int64(int64_t) { return static_cast<Override&>(*this).Default(); }
+ bool Uint64(uint64_t) { return static_cast<Override&>(*this).Default(); }
+ bool Double(double) { return static_cast<Override&>(*this).Default(); }
+ /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length)
+ bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast<Override&>(*this).String(str, len, copy); }
+ bool String(const Ch*, SizeType, bool) { return static_cast<Override&>(*this).Default(); }
+ bool StartObject() { return static_cast<Override&>(*this).Default(); }
+ bool Key(const Ch* str, SizeType len, bool copy) { return static_cast<Override&>(*this).String(str, len, copy); }
+ bool EndObject(SizeType) { return static_cast<Override&>(*this).Default(); }
+ bool StartArray() { return static_cast<Override&>(*this).Default(); }
+ bool EndArray(SizeType) { return static_cast<Override&>(*this).Default(); }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamLocalCopy
+
+namespace internal {
+
+template<typename Stream, int = StreamTraits<Stream>::copyOptimization>
+class StreamLocalCopy;
+
+//! Do copy optimization.
+template<typename Stream>
+class StreamLocalCopy<Stream, 1> {
+public:
+ StreamLocalCopy(Stream& original) : s(original), original_(original) {}
+ ~StreamLocalCopy() { original_ = s; }
+
+ Stream s;
+
+private:
+ StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */;
+
+ Stream& original_;
+};
+
+//! Keep reference.
+template<typename Stream>
+class StreamLocalCopy<Stream, 0> {
+public:
+ StreamLocalCopy(Stream& original) : s(original) {}
+
+ Stream& s;
+
+private:
+ StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */;
+};
+
+} // namespace internal
+
+///////////////////////////////////////////////////////////////////////////////
+// SkipWhitespace
+
+//! Skip the JSON white spaces in a stream.
+/*! \param is A input stream for skipping white spaces.
+ \note This function has SSE2/SSE4.2 specialization.
+*/
+template<typename InputStream>
+void SkipWhitespace(InputStream& is) {
+ internal::StreamLocalCopy<InputStream> copy(is);
+ InputStream& s(copy.s);
+
+ typename InputStream::Ch c;
+ while ((c = s.Peek()) == ' ' || c == '\n' || c == '\r' || c == '\t')
+ s.Take();
+}
+
+inline const char* SkipWhitespace(const char* p, const char* end) {
+ while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t'))
+ ++p;
+ return p;
+}
+
+#ifdef RAPIDJSON_SSE42
+//! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once.
+inline const char *SkipWhitespace_SIMD(const char* p) {
+ // Fast return for single non-whitespace
+ if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')
+ ++p;
+ else
+ return p;
+
+ // 16-byte align to the next boundary
+ const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+ while (p != nextAligned)
+ if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')
+ ++p;
+ else
+ return p;
+
+ // The rest of string using SIMD
+ static const char whitespace[16] = " \n\r\t";
+ const __m128i w = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespace[0]));
+
+ for (;; p += 16) {
+ const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
+ const int r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY));
+ if (r != 0) { // some of characters is non-whitespace
+#ifdef _MSC_VER // Find the index of first non-whitespace
+ unsigned long offset;
+ _BitScanForward(&offset, r);
+ return p + offset;
+#else
+ return p + __builtin_ffs(r) - 1;
+#endif
+ }
+ }
+}
+
+inline const char *SkipWhitespace_SIMD(const char* p, const char* end) {
+ // Fast return for single non-whitespace
+ if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t'))
+ ++p;
+ else
+ return p;
+
+ // The middle of string using SIMD
+ static const char whitespace[16] = " \n\r\t";
+ const __m128i w = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespace[0]));
+
+ for (; p <= end - 16; p += 16) {
+ const __m128i s = _mm_loadu_si128(reinterpret_cast<const __m128i *>(p));
+ const int r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY));
+ if (r != 0) { // some of characters is non-whitespace
+#ifdef _MSC_VER // Find the index of first non-whitespace
+ unsigned long offset;
+ _BitScanForward(&offset, r);
+ return p + offset;
+#else
+ return p + __builtin_ffs(r) - 1;
+#endif
+ }
+ }
+
+ return SkipWhitespace(p, end);
+}
+
+#elif defined(RAPIDJSON_SSE2)
+
+//! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once.
+inline const char *SkipWhitespace_SIMD(const char* p) {
+ // Fast return for single non-whitespace
+ if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')
+ ++p;
+ else
+ return p;
+
+ // 16-byte align to the next boundary
+ const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+ while (p != nextAligned)
+ if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')
+ ++p;
+ else
+ return p;
+
+ // The rest of string
+ #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c }
+ static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') };
+ #undef C16
+
+ const __m128i w0 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[0][0]));
+ const __m128i w1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[1][0]));
+ const __m128i w2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[2][0]));
+ const __m128i w3 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[3][0]));
+
+ for (;; p += 16) {
+ const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
+ __m128i x = _mm_cmpeq_epi8(s, w0);
+ x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1));
+ x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2));
+ x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3));
+ unsigned short r = static_cast<unsigned short>(~_mm_movemask_epi8(x));
+ if (r != 0) { // some of characters may be non-whitespace
+#ifdef _MSC_VER // Find the index of first non-whitespace
+ unsigned long offset;
+ _BitScanForward(&offset, r);
+ return p + offset;
+#else
+ return p + __builtin_ffs(r) - 1;
+#endif
+ }
+ }
+}
+
+inline const char *SkipWhitespace_SIMD(const char* p, const char* end) {
+ // Fast return for single non-whitespace
+ if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t'))
+ ++p;
+ else
+ return p;
+
+ // The rest of string
+ #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c }
+ static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') };
+ #undef C16
+
+ const __m128i w0 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[0][0]));
+ const __m128i w1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[1][0]));
+ const __m128i w2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[2][0]));
+ const __m128i w3 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[3][0]));
+
+ for (; p <= end - 16; p += 16) {
+ const __m128i s = _mm_loadu_si128(reinterpret_cast<const __m128i *>(p));
+ __m128i x = _mm_cmpeq_epi8(s, w0);
+ x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1));
+ x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2));
+ x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3));
+ unsigned short r = static_cast<unsigned short>(~_mm_movemask_epi8(x));
+ if (r != 0) { // some of characters may be non-whitespace
+#ifdef _MSC_VER // Find the index of first non-whitespace
+ unsigned long offset;
+ _BitScanForward(&offset, r);
+ return p + offset;
+#else
+ return p + __builtin_ffs(r) - 1;
+#endif
+ }
+ }
+
+ return SkipWhitespace(p, end);
+}
+
+#endif // RAPIDJSON_SSE2
+
+#ifdef RAPIDJSON_SIMD
+//! Template function specialization for InsituStringStream
+template<> inline void SkipWhitespace(InsituStringStream& is) {
+ is.src_ = const_cast<char*>(SkipWhitespace_SIMD(is.src_));
+}
+
+//! Template function specialization for StringStream
+template<> inline void SkipWhitespace(StringStream& is) {
+ is.src_ = SkipWhitespace_SIMD(is.src_);
+}
+
+template<> inline void SkipWhitespace(EncodedInputStream<UTF8<>, MemoryStream>& is) {
+ is.is_.src_ = SkipWhitespace_SIMD(is.is_.src_, is.is_.end_);
+}
+#endif // RAPIDJSON_SIMD
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericReader
+
+//! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator.
+/*! GenericReader parses JSON text from a stream, and send events synchronously to an
+ object implementing Handler concept.
+
+ It needs to allocate a stack for storing a single decoded string during
+ non-destructive parsing.
+
+ For in-situ parsing, the decoded string is directly written to the source
+ text string, no temporary buffer is required.
+
+ A GenericReader object can be reused for parsing multiple JSON text.
+
+ \tparam SourceEncoding Encoding of the input stream.
+ \tparam TargetEncoding Encoding of the parse output.
+ \tparam StackAllocator Allocator type for stack.
+*/
+template <typename SourceEncoding, typename TargetEncoding, typename StackAllocator = CrtAllocator>
+class GenericReader {
+public:
+ typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type
+
+ //! Constructor.
+ /*! \param stackAllocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing)
+ \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing)
+ */
+ GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(stackAllocator, stackCapacity), parseResult_() {}
+
+ //! Parse JSON text.
+ /*! \tparam parseFlags Combination of \ref ParseFlag.
+ \tparam InputStream Type of input stream, implementing Stream concept.
+ \tparam Handler Type of handler, implementing Handler concept.
+ \param is Input stream to be parsed.
+ \param handler The handler to receive events.
+ \return Whether the parsing is successful.
+ */
+ template <unsigned parseFlags, typename InputStream, typename Handler>
+ ParseResult Parse(InputStream& is, Handler& handler) {
+ if (parseFlags & kParseIterativeFlag)
+ return IterativeParse<parseFlags>(is, handler);
+
+ parseResult_.Clear();
+
+ ClearStackOnExit scope(*this);
+
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
+
+ if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) {
+ RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell());
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
+ }
+ else {
+ ParseValue<parseFlags>(is, handler);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
+
+ if (!(parseFlags & kParseStopWhenDoneFlag)) {
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
+
+ if (RAPIDJSON_UNLIKELY(is.Peek() != '\0')) {
+ RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell());
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
+ }
+ }
+ }
+
+ return parseResult_;
+ }
+
+ //! Parse JSON text (with \ref kParseDefaultFlags)
+ /*! \tparam InputStream Type of input stream, implementing Stream concept
+ \tparam Handler Type of handler, implementing Handler concept.
+ \param is Input stream to be parsed.
+ \param handler The handler to receive events.
+ \return Whether the parsing is successful.
+ */
+ template <typename InputStream, typename Handler>
+ ParseResult Parse(InputStream& is, Handler& handler) {
+ return Parse<kParseDefaultFlags>(is, handler);
+ }
+
+ //! Whether a parse error has occured in the last parsing.
+ bool HasParseError() const { return parseResult_.IsError(); }
+
+ //! Get the \ref ParseErrorCode of last parsing.
+ ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); }
+
+ //! Get the position of last parsing error in input, 0 otherwise.
+ size_t GetErrorOffset() const { return parseResult_.Offset(); }
+
+protected:
+ void SetParseError(ParseErrorCode code, size_t offset) { parseResult_.Set(code, offset); }
+
+private:
+ // Prohibit copy constructor & assignment operator.
+ GenericReader(const GenericReader&);
+ GenericReader& operator=(const GenericReader&);
+
+ void ClearStack() { stack_.Clear(); }
+
+ // clear stack on any exit from ParseStream, e.g. due to exception
+ struct ClearStackOnExit {
+ explicit ClearStackOnExit(GenericReader& r) : r_(r) {}
+ ~ClearStackOnExit() { r_.ClearStack(); }
+ private:
+ GenericReader& r_;
+ ClearStackOnExit(const ClearStackOnExit&);
+ ClearStackOnExit& operator=(const ClearStackOnExit&);
+ };
+
+ template<unsigned parseFlags, typename InputStream>
+ void SkipWhitespaceAndComments(InputStream& is) {
+ SkipWhitespace(is);
+
+ if (parseFlags & kParseCommentsFlag) {
+ while (RAPIDJSON_UNLIKELY(Consume(is, '/'))) {
+ if (Consume(is, '*')) {
+ while (true) {
+ if (RAPIDJSON_UNLIKELY(is.Peek() == '\0'))
+ RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell());
+ else if (Consume(is, '*')) {
+ if (Consume(is, '/'))
+ break;
+ }
+ else
+ is.Take();
+ }
+ }
+ else if (RAPIDJSON_LIKELY(Consume(is, '/')))
+ while (is.Peek() != '\0' && is.Take() != '\n');
+ else
+ RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell());
+
+ SkipWhitespace(is);
+ }
+ }
+ }
+
+ // Parse object: { string : value, ... }
+ template<unsigned parseFlags, typename InputStream, typename Handler>
+ void ParseObject(InputStream& is, Handler& handler) {
+ RAPIDJSON_ASSERT(is.Peek() == '{');
+ is.Take(); // Skip '{'
+
+ if (RAPIDJSON_UNLIKELY(!handler.StartObject()))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+ if (Consume(is, '}')) {
+ if (RAPIDJSON_UNLIKELY(!handler.EndObject(0))) // empty object
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+ return;
+ }
+
+ for (SizeType memberCount = 0;;) {
+ if (RAPIDJSON_UNLIKELY(is.Peek() != '"'))
+ RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell());
+
+ ParseString<parseFlags>(is, handler, true);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+ if (RAPIDJSON_UNLIKELY(!Consume(is, ':')))
+ RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell());
+
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+ ParseValue<parseFlags>(is, handler);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+ ++memberCount;
+
+ switch (is.Peek()) {
+ case ',':
+ is.Take();
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+ break;
+ case '}':
+ is.Take();
+ if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount)))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+ return;
+ default:
+ RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); break; // This useless break is only for making warning and coverage happy
+ }
+
+ if (parseFlags & kParseTrailingCommasFlag) {
+ if (is.Peek() == '}') {
+ if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount)))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+ is.Take();
+ return;
+ }
+ }
+ }
+ }
+
+ // Parse array: [ value, ... ]
+ template<unsigned parseFlags, typename InputStream, typename Handler>
+ void ParseArray(InputStream& is, Handler& handler) {
+ RAPIDJSON_ASSERT(is.Peek() == '[');
+ is.Take(); // Skip '['
+
+ if (RAPIDJSON_UNLIKELY(!handler.StartArray()))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+ if (Consume(is, ']')) {
+ if (RAPIDJSON_UNLIKELY(!handler.EndArray(0))) // empty array
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+ return;
+ }
+
+ for (SizeType elementCount = 0;;) {
+ ParseValue<parseFlags>(is, handler);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+ ++elementCount;
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+ if (Consume(is, ',')) {
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+ }
+ else if (Consume(is, ']')) {
+ if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount)))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+ return;
+ }
+ else
+ RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell());
+
+ if (parseFlags & kParseTrailingCommasFlag) {
+ if (is.Peek() == ']') {
+ if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount)))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+ is.Take();
+ return;
+ }
+ }
+ }
+ }
+
+ template<unsigned parseFlags, typename InputStream, typename Handler>
+ void ParseNull(InputStream& is, Handler& handler) {
+ RAPIDJSON_ASSERT(is.Peek() == 'n');
+ is.Take();
+
+ if (RAPIDJSON_LIKELY(Consume(is, 'u') && Consume(is, 'l') && Consume(is, 'l'))) {
+ if (RAPIDJSON_UNLIKELY(!handler.Null()))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+ }
+ else
+ RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell());
+ }
+
+ template<unsigned parseFlags, typename InputStream, typename Handler>
+ void ParseTrue(InputStream& is, Handler& handler) {
+ RAPIDJSON_ASSERT(is.Peek() == 't');
+ is.Take();
+
+ if (RAPIDJSON_LIKELY(Consume(is, 'r') && Consume(is, 'u') && Consume(is, 'e'))) {
+ if (RAPIDJSON_UNLIKELY(!handler.Bool(true)))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+ }
+ else
+ RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell());
+ }
+
+ template<unsigned parseFlags, typename InputStream, typename Handler>
+ void ParseFalse(InputStream& is, Handler& handler) {
+ RAPIDJSON_ASSERT(is.Peek() == 'f');
+ is.Take();
+
+ if (RAPIDJSON_LIKELY(Consume(is, 'a') && Consume(is, 'l') && Consume(is, 's') && Consume(is, 'e'))) {
+ if (RAPIDJSON_UNLIKELY(!handler.Bool(false)))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+ }
+ else
+ RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell());
+ }
+
+ template<typename InputStream>
+ RAPIDJSON_FORCEINLINE static bool Consume(InputStream& is, typename InputStream::Ch expect) {
+ if (RAPIDJSON_LIKELY(is.Peek() == expect)) {
+ is.Take();
+ return true;
+ }
+ else
+ return false;
+ }
+
+ // Helper function to parse four hexidecimal digits in \uXXXX in ParseString().
+ template<typename InputStream>
+ unsigned ParseHex4(InputStream& is, size_t escapeOffset) {
+ unsigned codepoint = 0;
+ for (int i = 0; i < 4; i++) {
+ Ch c = is.Peek();
+ codepoint <<= 4;
+ codepoint += static_cast<unsigned>(c);
+ if (c >= '0' && c <= '9')
+ codepoint -= '0';
+ else if (c >= 'A' && c <= 'F')
+ codepoint -= 'A' - 10;
+ else if (c >= 'a' && c <= 'f')
+ codepoint -= 'a' - 10;
+ else {
+ RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, escapeOffset);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0);
+ }
+ is.Take();
+ }
+ return codepoint;
+ }
+
+ template <typename CharType>
+ class StackStream {
+ public:
+ typedef CharType Ch;
+
+ StackStream(internal::Stack<StackAllocator>& stack) : stack_(stack), length_(0) {}
+ RAPIDJSON_FORCEINLINE void Put(Ch c) {
+ *stack_.template Push<Ch>() = c;
+ ++length_;
+ }
+
+ RAPIDJSON_FORCEINLINE void* Push(SizeType count) {
+ length_ += count;
+ return stack_.template Push<Ch>(count);
+ }
+
+ size_t Length() const { return length_; }
+
+ Ch* Pop() {
+ return stack_.template Pop<Ch>(length_);
+ }
+
+ private:
+ StackStream(const StackStream&);
+ StackStream& operator=(const StackStream&);
+
+ internal::Stack<StackAllocator>& stack_;
+ SizeType length_;
+ };
+
+ // Parse string and generate String event. Different code paths for kParseInsituFlag.
+ template<unsigned parseFlags, typename InputStream, typename Handler>
+ void ParseString(InputStream& is, Handler& handler, bool isKey = false) {
+ internal::StreamLocalCopy<InputStream> copy(is);
+ InputStream& s(copy.s);
+
+ RAPIDJSON_ASSERT(s.Peek() == '\"');
+ s.Take(); // Skip '\"'
+
+ bool success = false;
+ if (parseFlags & kParseInsituFlag) {
+ typename InputStream::Ch *head = s.PutBegin();
+ ParseStringToStream<parseFlags, SourceEncoding, SourceEncoding>(s, s);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+ size_t length = s.PutEnd(head) - 1;
+ RAPIDJSON_ASSERT(length <= 0xFFFFFFFF);
+ const typename TargetEncoding::Ch* const str = reinterpret_cast<typename TargetEncoding::Ch*>(head);
+ success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false));
+ }
+ else {
+ StackStream<typename TargetEncoding::Ch> stackStream(stack_);
+ ParseStringToStream<parseFlags, SourceEncoding, TargetEncoding>(s, stackStream);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+ SizeType length = static_cast<SizeType>(stackStream.Length()) - 1;
+ const typename TargetEncoding::Ch* const str = stackStream.Pop();
+ success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true));
+ }
+ if (RAPIDJSON_UNLIKELY(!success))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell());
+ }
+
+ // Parse string to an output is
+ // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation.
+ template<unsigned parseFlags, typename SEncoding, typename TEncoding, typename InputStream, typename OutputStream>
+ RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) {
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ static const char escape[256] = {
+ Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/',
+ Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0,
+ 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0,
+ 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16
+ };
+#undef Z16
+//!@endcond
+
+ for (;;) {
+ // Scan and copy string before "\\\"" or < 0x20. This is an optional optimzation.
+ if (!(parseFlags & kParseValidateEncodingFlag))
+ ScanCopyUnescapedString(is, os);
+
+ Ch c = is.Peek();
+ if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape
+ size_t escapeOffset = is.Tell(); // For invalid escaping, report the inital '\\' as error offset
+ is.Take();
+ Ch e = is.Peek();
+ if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast<unsigned char>(e)])) {
+ is.Take();
+ os.Put(static_cast<typename TEncoding::Ch>(escape[static_cast<unsigned char>(e)]));
+ }
+ else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode
+ is.Take();
+ unsigned codepoint = ParseHex4(is, escapeOffset);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+ if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDBFF)) {
+ // Handle UTF-16 surrogate pair
+ if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u')))
+ RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset);
+ unsigned codepoint2 = ParseHex4(is, escapeOffset);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+ if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF))
+ RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset);
+ codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000;
+ }
+ TEncoding::Encode(os, codepoint);
+ }
+ else
+ RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset);
+ }
+ else if (RAPIDJSON_UNLIKELY(c == '"')) { // Closing double quote
+ is.Take();
+ os.Put('\0'); // null-terminate the string
+ return;
+ }
+ else if (RAPIDJSON_UNLIKELY(static_cast<unsigned>(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
+ if (c == '\0')
+ RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell());
+ else
+ RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell());
+ }
+ else {
+ size_t offset = is.Tell();
+ if (RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ?
+ !Transcoder<SEncoding, TEncoding>::Validate(is, os) :
+ !Transcoder<SEncoding, TEncoding>::Transcode(is, os))))
+ RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset);
+ }
+ }
+ }
+
+ template<typename InputStream, typename OutputStream>
+ static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InputStream&, OutputStream&) {
+ // Do nothing for generic version
+ }
+
+#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42)
+ // StringStream -> StackStream<char>
+ static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream<char>& os) {
+ const char* p = is.src_;
+
+ // Scan one by one until alignment (unaligned load may cross page boundary and cause crash)
+ const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+ while (p != nextAligned)
+ if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*p) < 0x20)) {
+ is.src_ = p;
+ return;
+ }
+ else
+ os.Put(*p++);
+
+ // The rest of string using SIMD
+ static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' };
+ static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' };
+ static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 };
+ const __m128i dq = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&dquote[0]));
+ const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
+ const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
+
+ for (;; p += 16) {
+ const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
+ const __m128i t1 = _mm_cmpeq_epi8(s, dq);
+ const __m128i t2 = _mm_cmpeq_epi8(s, bs);
+ const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19
+ const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3);
+ unsigned short r = static_cast<unsigned short>(_mm_movemask_epi8(x));
+ if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped
+ SizeType length;
+ #ifdef _MSC_VER // Find the index of first escaped
+ unsigned long offset;
+ _BitScanForward(&offset, r);
+ length = offset;
+ #else
+ length = static_cast<SizeType>(__builtin_ffs(r) - 1);
+ #endif
+ char* q = reinterpret_cast<char*>(os.Push(length));
+ for (size_t i = 0; i < length; i++)
+ q[i] = p[i];
+
+ p += length;
+ break;
+ }
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s);
+ }
+
+ is.src_ = p;
+ }
+
+ // InsituStringStream -> InsituStringStream
+ static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) {
+ RAPIDJSON_ASSERT(&is == &os);
+ (void)os;
+
+ if (is.src_ == is.dst_) {
+ SkipUnescapedString(is);
+ return;
+ }
+
+ char* p = is.src_;
+ char *q = is.dst_;
+
+ // Scan one by one until alignment (unaligned load may cross page boundary and cause crash)
+ const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+ while (p != nextAligned)
+ if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*p) < 0x20)) {
+ is.src_ = p;
+ is.dst_ = q;
+ return;
+ }
+ else
+ *q++ = *p++;
+
+ // The rest of string using SIMD
+ static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' };
+ static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' };
+ static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 };
+ const __m128i dq = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&dquote[0]));
+ const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
+ const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
+
+ for (;; p += 16, q += 16) {
+ const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
+ const __m128i t1 = _mm_cmpeq_epi8(s, dq);
+ const __m128i t2 = _mm_cmpeq_epi8(s, bs);
+ const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19
+ const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3);
+ unsigned short r = static_cast<unsigned short>(_mm_movemask_epi8(x));
+ if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped
+ size_t length;
+#ifdef _MSC_VER // Find the index of first escaped
+ unsigned long offset;
+ _BitScanForward(&offset, r);
+ length = offset;
+#else
+ length = static_cast<size_t>(__builtin_ffs(r) - 1);
+#endif
+ for (const char* pend = p + length; p != pend; )
+ *q++ = *p++;
+ break;
+ }
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(q), s);
+ }
+
+ is.src_ = p;
+ is.dst_ = q;
+ }
+
+ // When read/write pointers are the same for insitu stream, just skip unescaped characters
+ static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) {
+ RAPIDJSON_ASSERT(is.src_ == is.dst_);
+ char* p = is.src_;
+
+ // Scan one by one until alignment (unaligned load may cross page boundary and cause crash)
+ const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+ for (; p != nextAligned; p++)
+ if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*p) < 0x20)) {
+ is.src_ = is.dst_ = p;
+ return;
+ }
+
+ // The rest of string using SIMD
+ static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' };
+ static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' };
+ static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 };
+ const __m128i dq = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&dquote[0]));
+ const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
+ const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
+
+ for (;; p += 16) {
+ const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
+ const __m128i t1 = _mm_cmpeq_epi8(s, dq);
+ const __m128i t2 = _mm_cmpeq_epi8(s, bs);
+ const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19
+ const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3);
+ unsigned short r = static_cast<unsigned short>(_mm_movemask_epi8(x));
+ if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped
+ size_t length;
+#ifdef _MSC_VER // Find the index of first escaped
+ unsigned long offset;
+ _BitScanForward(&offset, r);
+ length = offset;
+#else
+ length = static_cast<size_t>(__builtin_ffs(r) - 1);
+#endif
+ p += length;
+ break;
+ }
+ }
+
+ is.src_ = is.dst_ = p;
+ }
+#endif
+
+ template<typename InputStream, bool backup, bool pushOnTake>
+ class NumberStream;
+
+ template<typename InputStream>
+ class NumberStream<InputStream, false, false> {
+ public:
+ typedef typename InputStream::Ch Ch;
+
+ NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; }
+ ~NumberStream() {}
+
+ RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); }
+ RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); }
+ RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); }
+ RAPIDJSON_FORCEINLINE void Push(char) {}
+
+ size_t Tell() { return is.Tell(); }
+ size_t Length() { return 0; }
+ const char* Pop() { return 0; }
+
+ protected:
+ NumberStream& operator=(const NumberStream&);
+
+ InputStream& is;
+ };
+
+ template<typename InputStream>
+ class NumberStream<InputStream, true, false> : public NumberStream<InputStream, false, false> {
+ typedef NumberStream<InputStream, false, false> Base;
+ public:
+ NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is), stackStream(reader.stack_) {}
+ ~NumberStream() {}
+
+ RAPIDJSON_FORCEINLINE Ch TakePush() {
+ stackStream.Put(static_cast<char>(Base::is.Peek()));
+ return Base::is.Take();
+ }
+
+ RAPIDJSON_FORCEINLINE void Push(char c) {
+ stackStream.Put(c);
+ }
+
+ size_t Length() { return stackStream.Length(); }
+
+ const char* Pop() {
+ stackStream.Put('\0');
+ return stackStream.Pop();
+ }
+
+ private:
+ StackStream<char> stackStream;
+ };
+
+ template<typename InputStream>
+ class NumberStream<InputStream, true, true> : public NumberStream<InputStream, true, false> {
+ typedef NumberStream<InputStream, true, false> Base;
+ public:
+ NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {}
+ ~NumberStream() {}
+
+ RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); }
+ };
+
+ template<unsigned parseFlags, typename InputStream, typename Handler>
+ void ParseNumber(InputStream& is, Handler& handler) {
+ internal::StreamLocalCopy<InputStream> copy(is);
+ NumberStream<InputStream,
+ ((parseFlags & kParseNumbersAsStringsFlag) != 0) ?
+ ((parseFlags & kParseInsituFlag) == 0) :
+ ((parseFlags & kParseFullPrecisionFlag) != 0),
+ (parseFlags & kParseNumbersAsStringsFlag) != 0 &&
+ (parseFlags & kParseInsituFlag) == 0> s(*this, copy.s);
+
+ size_t startOffset = s.Tell();
+ double d = 0.0;
+ bool useNanOrInf = false;
+
+ // Parse minus
+ bool minus = Consume(s, '-');
+
+ // Parse int: zero / ( digit1-9 *DIGIT )
+ unsigned i = 0;
+ uint64_t i64 = 0;
+ bool use64bit = false;
+ int significandDigit = 0;
+ if (RAPIDJSON_UNLIKELY(s.Peek() == '0')) {
+ i = 0;
+ s.TakePush();
+ }
+ else if (RAPIDJSON_LIKELY(s.Peek() >= '1' && s.Peek() <= '9')) {
+ i = static_cast<unsigned>(s.TakePush() - '0');
+
+ if (minus)
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ if (RAPIDJSON_UNLIKELY(i >= 214748364)) { // 2^31 = 2147483648
+ if (RAPIDJSON_LIKELY(i != 214748364 || s.Peek() > '8')) {
+ i64 = i;
+ use64bit = true;
+ break;
+ }
+ }
+ i = i * 10 + static_cast<unsigned>(s.TakePush() - '0');
+ significandDigit++;
+ }
+ else
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ if (RAPIDJSON_UNLIKELY(i >= 429496729)) { // 2^32 - 1 = 4294967295
+ if (RAPIDJSON_LIKELY(i != 429496729 || s.Peek() > '5')) {
+ i64 = i;
+ use64bit = true;
+ break;
+ }
+ }
+ i = i * 10 + static_cast<unsigned>(s.TakePush() - '0');
+ significandDigit++;
+ }
+ }
+ // Parse NaN or Infinity here
+ else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) {
+ useNanOrInf = true;
+ if (RAPIDJSON_LIKELY(Consume(s, 'N') && Consume(s, 'a') && Consume(s, 'N'))) {
+ d = std::numeric_limits<double>::quiet_NaN();
+ }
+ else if (RAPIDJSON_LIKELY(Consume(s, 'I') && Consume(s, 'n') && Consume(s, 'f'))) {
+ d = (minus ? -std::numeric_limits<double>::infinity() : std::numeric_limits<double>::infinity());
+ if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n')
+ && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y'))))
+ RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell());
+ }
+ else
+ RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell());
+ }
+ else
+ RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell());
+
+ // Parse 64bit int
+ bool useDouble = false;
+ if (use64bit) {
+ if (minus)
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC))) // 2^63 = 9223372036854775808
+ if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8')) {
+ d = static_cast<double>(i64);
+ useDouble = true;
+ break;
+ }
+ i64 = i64 * 10 + static_cast<unsigned>(s.TakePush() - '0');
+ significandDigit++;
+ }
+ else
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999))) // 2^64 - 1 = 18446744073709551615
+ if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5')) {
+ d = static_cast<double>(i64);
+ useDouble = true;
+ break;
+ }
+ i64 = i64 * 10 + static_cast<unsigned>(s.TakePush() - '0');
+ significandDigit++;
+ }
+ }
+
+ // Force double for big integer
+ if (useDouble) {
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ if (RAPIDJSON_UNLIKELY(d >= 1.7976931348623157e307)) // DBL_MAX / 10.0
+ RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset);
+ d = d * 10 + (s.TakePush() - '0');
+ }
+ }
+
+ // Parse frac = decimal-point 1*DIGIT
+ int expFrac = 0;
+ size_t decimalPosition;
+ if (Consume(s, '.')) {
+ decimalPosition = s.Length();
+
+ if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9')))
+ RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell());
+
+ if (!useDouble) {
+#if RAPIDJSON_64BIT
+ // Use i64 to store significand in 64-bit architecture
+ if (!use64bit)
+ i64 = i;
+
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path
+ break;
+ else {
+ i64 = i64 * 10 + static_cast<unsigned>(s.TakePush() - '0');
+ --expFrac;
+ if (i64 != 0)
+ significandDigit++;
+ }
+ }
+
+ d = static_cast<double>(i64);
+#else
+ // Use double to store significand in 32-bit architecture
+ d = static_cast<double>(use64bit ? i64 : i);
+#endif
+ useDouble = true;
+ }
+
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ if (significandDigit < 17) {
+ d = d * 10.0 + (s.TakePush() - '0');
+ --expFrac;
+ if (RAPIDJSON_LIKELY(d > 0.0))
+ significandDigit++;
+ }
+ else
+ s.TakePush();
+ }
+ }
+ else
+ decimalPosition = s.Length(); // decimal position at the end of integer.
+
+ // Parse exp = e [ minus / plus ] 1*DIGIT
+ int exp = 0;
+ if (Consume(s, 'e') || Consume(s, 'E')) {
+ if (!useDouble) {
+ d = static_cast<double>(use64bit ? i64 : i);
+ useDouble = true;
+ }
+
+ bool expMinus = false;
+ if (Consume(s, '+'))
+ ;
+ else if (Consume(s, '-'))
+ expMinus = true;
+
+ if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ exp = static_cast<int>(s.Take() - '0');
+ if (expMinus) {
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ exp = exp * 10 + static_cast<int>(s.Take() - '0');
+ if (exp >= 214748364) { // Issue #313: prevent overflow exponent
+ while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent
+ s.Take();
+ }
+ }
+ }
+ else { // positive exp
+ int maxExp = 308 - expFrac;
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ exp = exp * 10 + static_cast<int>(s.Take() - '0');
+ if (RAPIDJSON_UNLIKELY(exp > maxExp))
+ RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset);
+ }
+ }
+ }
+ else
+ RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell());
+
+ if (expMinus)
+ exp = -exp;
+ }
+
+ // Finish parsing, call event according to the type of number.
+ bool cont = true;
+
+ if (parseFlags & kParseNumbersAsStringsFlag) {
+ if (parseFlags & kParseInsituFlag) {
+ s.Pop(); // Pop stack no matter if it will be used or not.
+ typename InputStream::Ch* head = is.PutBegin();
+ const size_t length = s.Tell() - startOffset;
+ RAPIDJSON_ASSERT(length <= 0xFFFFFFFF);
+ // unable to insert the \0 character here, it will erase the comma after this number
+ const typename TargetEncoding::Ch* const str = reinterpret_cast<typename TargetEncoding::Ch*>(head);
+ cont = handler.RawNumber(str, SizeType(length), false);
+ }
+ else {
+ SizeType numCharsToCopy = static_cast<SizeType>(s.Length());
+ StringStream srcStream(s.Pop());
+ StackStream<typename TargetEncoding::Ch> dstStream(stack_);
+ while (numCharsToCopy--) {
+ Transcoder<UTF8<>, TargetEncoding>::Transcode(srcStream, dstStream);
+ }
+ dstStream.Put('\0');
+ const typename TargetEncoding::Ch* str = dstStream.Pop();
+ const SizeType length = static_cast<SizeType>(dstStream.Length()) - 1;
+ cont = handler.RawNumber(str, SizeType(length), true);
+ }
+ }
+ else {
+ size_t length = s.Length();
+ const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not.
+
+ if (useDouble) {
+ int p = exp + expFrac;
+ if (parseFlags & kParseFullPrecisionFlag)
+ d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp);
+ else
+ d = internal::StrtodNormalPrecision(d, p);
+
+ cont = handler.Double(minus ? -d : d);
+ }
+ else if (useNanOrInf) {
+ cont = handler.Double(d);
+ }
+ else {
+ if (use64bit) {
+ if (minus)
+ cont = handler.Int64(static_cast<int64_t>(~i64 + 1));
+ else
+ cont = handler.Uint64(i64);
+ }
+ else {
+ if (minus)
+ cont = handler.Int(static_cast<int32_t>(~i + 1));
+ else
+ cont = handler.Uint(i);
+ }
+ }
+ }
+ if (RAPIDJSON_UNLIKELY(!cont))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset);
+ }
+
+ // Parse any JSON value
+ template<unsigned parseFlags, typename InputStream, typename Handler>
+ void ParseValue(InputStream& is, Handler& handler) {
+ switch (is.Peek()) {
+ case 'n': ParseNull <parseFlags>(is, handler); break;
+ case 't': ParseTrue <parseFlags>(is, handler); break;
+ case 'f': ParseFalse <parseFlags>(is, handler); break;
+ case '"': ParseString<parseFlags>(is, handler); break;
+ case '{': ParseObject<parseFlags>(is, handler); break;
+ case '[': ParseArray <parseFlags>(is, handler); break;
+ default :
+ ParseNumber<parseFlags>(is, handler);
+ break;
+
+ }
+ }
+
+ // Iterative Parsing
+
+ // States
+ enum IterativeParsingState {
+ IterativeParsingStartState = 0,
+ IterativeParsingFinishState,
+ IterativeParsingErrorState,
+
+ // Object states
+ IterativeParsingObjectInitialState,
+ IterativeParsingMemberKeyState,
+ IterativeParsingKeyValueDelimiterState,
+ IterativeParsingMemberValueState,
+ IterativeParsingMemberDelimiterState,
+ IterativeParsingObjectFinishState,
+
+ // Array states
+ IterativeParsingArrayInitialState,
+ IterativeParsingElementState,
+ IterativeParsingElementDelimiterState,
+ IterativeParsingArrayFinishState,
+
+ // Single value state
+ IterativeParsingValueState
+ };
+
+ enum { cIterativeParsingStateCount = IterativeParsingValueState + 1 };
+
+ // Tokens
+ enum Token {
+ LeftBracketToken = 0,
+ RightBracketToken,
+
+ LeftCurlyBracketToken,
+ RightCurlyBracketToken,
+
+ CommaToken,
+ ColonToken,
+
+ StringToken,
+ FalseToken,
+ TrueToken,
+ NullToken,
+ NumberToken,
+
+ kTokenCount
+ };
+
+ RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) {
+
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+#define N NumberToken
+#define N16 N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N
+ // Maps from ASCII to Token
+ static const unsigned char tokenMap[256] = {
+ N16, // 00~0F
+ N16, // 10~1F
+ N, N, StringToken, N, N, N, N, N, N, N, N, N, CommaToken, N, N, N, // 20~2F
+ N, N, N, N, N, N, N, N, N, N, ColonToken, N, N, N, N, N, // 30~3F
+ N16, // 40~4F
+ N, N, N, N, N, N, N, N, N, N, N, LeftBracketToken, N, RightBracketToken, N, N, // 50~5F
+ N, N, N, N, N, N, FalseToken, N, N, N, N, N, N, N, NullToken, N, // 60~6F
+ N, N, N, N, TrueToken, N, N, N, N, N, N, LeftCurlyBracketToken, N, RightCurlyBracketToken, N, N, // 70~7F
+ N16, N16, N16, N16, N16, N16, N16, N16 // 80~FF
+ };
+#undef N
+#undef N16
+//!@endcond
+
+ if (sizeof(Ch) == 1 || static_cast<unsigned>(c) < 256)
+ return static_cast<Token>(tokenMap[static_cast<unsigned char>(c)]);
+ else
+ return NumberToken;
+ }
+
+ RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) {
+ // current state x one lookahead token -> new state
+ static const char G[cIterativeParsingStateCount][kTokenCount] = {
+ // Start
+ {
+ IterativeParsingArrayInitialState, // Left bracket
+ IterativeParsingErrorState, // Right bracket
+ IterativeParsingObjectInitialState, // Left curly bracket
+ IterativeParsingErrorState, // Right curly bracket
+ IterativeParsingErrorState, // Comma
+ IterativeParsingErrorState, // Colon
+ IterativeParsingValueState, // String
+ IterativeParsingValueState, // False
+ IterativeParsingValueState, // True
+ IterativeParsingValueState, // Null
+ IterativeParsingValueState // Number
+ },
+ // Finish(sink state)
+ {
+ IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+ IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+ IterativeParsingErrorState
+ },
+ // Error(sink state)
+ {
+ IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+ IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+ IterativeParsingErrorState
+ },
+ // ObjectInitial
+ {
+ IterativeParsingErrorState, // Left bracket
+ IterativeParsingErrorState, // Right bracket
+ IterativeParsingErrorState, // Left curly bracket
+ IterativeParsingObjectFinishState, // Right curly bracket
+ IterativeParsingErrorState, // Comma
+ IterativeParsingErrorState, // Colon
+ IterativeParsingMemberKeyState, // String
+ IterativeParsingErrorState, // False
+ IterativeParsingErrorState, // True
+ IterativeParsingErrorState, // Null
+ IterativeParsingErrorState // Number
+ },
+ // MemberKey
+ {
+ IterativeParsingErrorState, // Left bracket
+ IterativeParsingErrorState, // Right bracket
+ IterativeParsingErrorState, // Left curly bracket
+ IterativeParsingErrorState, // Right curly bracket
+ IterativeParsingErrorState, // Comma
+ IterativeParsingKeyValueDelimiterState, // Colon
+ IterativeParsingErrorState, // String
+ IterativeParsingErrorState, // False
+ IterativeParsingErrorState, // True
+ IterativeParsingErrorState, // Null
+ IterativeParsingErrorState // Number
+ },
+ // KeyValueDelimiter
+ {
+ IterativeParsingArrayInitialState, // Left bracket(push MemberValue state)
+ IterativeParsingErrorState, // Right bracket
+ IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state)
+ IterativeParsingErrorState, // Right curly bracket
+ IterativeParsingErrorState, // Comma
+ IterativeParsingErrorState, // Colon
+ IterativeParsingMemberValueState, // String
+ IterativeParsingMemberValueState, // False
+ IterativeParsingMemberValueState, // True
+ IterativeParsingMemberValueState, // Null
+ IterativeParsingMemberValueState // Number
+ },
+ // MemberValue
+ {
+ IterativeParsingErrorState, // Left bracket
+ IterativeParsingErrorState, // Right bracket
+ IterativeParsingErrorState, // Left curly bracket
+ IterativeParsingObjectFinishState, // Right curly bracket
+ IterativeParsingMemberDelimiterState, // Comma
+ IterativeParsingErrorState, // Colon
+ IterativeParsingErrorState, // String
+ IterativeParsingErrorState, // False
+ IterativeParsingErrorState, // True
+ IterativeParsingErrorState, // Null
+ IterativeParsingErrorState // Number
+ },
+ // MemberDelimiter
+ {
+ IterativeParsingErrorState, // Left bracket
+ IterativeParsingErrorState, // Right bracket
+ IterativeParsingErrorState, // Left curly bracket
+ IterativeParsingObjectFinishState, // Right curly bracket
+ IterativeParsingErrorState, // Comma
+ IterativeParsingErrorState, // Colon
+ IterativeParsingMemberKeyState, // String
+ IterativeParsingErrorState, // False
+ IterativeParsingErrorState, // True
+ IterativeParsingErrorState, // Null
+ IterativeParsingErrorState // Number
+ },
+ // ObjectFinish(sink state)
+ {
+ IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+ IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+ IterativeParsingErrorState
+ },
+ // ArrayInitial
+ {
+ IterativeParsingArrayInitialState, // Left bracket(push Element state)
+ IterativeParsingArrayFinishState, // Right bracket
+ IterativeParsingObjectInitialState, // Left curly bracket(push Element state)
+ IterativeParsingErrorState, // Right curly bracket
+ IterativeParsingErrorState, // Comma
+ IterativeParsingErrorState, // Colon
+ IterativeParsingElementState, // String
+ IterativeParsingElementState, // False
+ IterativeParsingElementState, // True
+ IterativeParsingElementState, // Null
+ IterativeParsingElementState // Number
+ },
+ // Element
+ {
+ IterativeParsingErrorState, // Left bracket
+ IterativeParsingArrayFinishState, // Right bracket
+ IterativeParsingErrorState, // Left curly bracket
+ IterativeParsingErrorState, // Right curly bracket
+ IterativeParsingElementDelimiterState, // Comma
+ IterativeParsingErrorState, // Colon
+ IterativeParsingErrorState, // String
+ IterativeParsingErrorState, // False
+ IterativeParsingErrorState, // True
+ IterativeParsingErrorState, // Null
+ IterativeParsingErrorState // Number
+ },
+ // ElementDelimiter
+ {
+ IterativeParsingArrayInitialState, // Left bracket(push Element state)
+ IterativeParsingArrayFinishState, // Right bracket
+ IterativeParsingObjectInitialState, // Left curly bracket(push Element state)
+ IterativeParsingErrorState, // Right curly bracket
+ IterativeParsingErrorState, // Comma
+ IterativeParsingErrorState, // Colon
+ IterativeParsingElementState, // String
+ IterativeParsingElementState, // False
+ IterativeParsingElementState, // True
+ IterativeParsingElementState, // Null
+ IterativeParsingElementState // Number
+ },
+ // ArrayFinish(sink state)
+ {
+ IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+ IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+ IterativeParsingErrorState
+ },
+ // Single Value (sink state)
+ {
+ IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+ IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+ IterativeParsingErrorState
+ }
+ }; // End of G
+
+ return static_cast<IterativeParsingState>(G[state][token]);
+ }
+
+ // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit().
+ // May return a new state on state pop.
+ template <unsigned parseFlags, typename InputStream, typename Handler>
+ RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) {
+ (void)token;
+
+ switch (dst) {
+ case IterativeParsingErrorState:
+ return dst;
+
+ case IterativeParsingObjectInitialState:
+ case IterativeParsingArrayInitialState:
+ {
+ // Push the state(Element or MemeberValue) if we are nested in another array or value of member.
+ // In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop.
+ IterativeParsingState n = src;
+ if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState)
+ n = IterativeParsingElementState;
+ else if (src == IterativeParsingKeyValueDelimiterState)
+ n = IterativeParsingMemberValueState;
+ // Push current state.
+ *stack_.template Push<SizeType>(1) = n;
+ // Initialize and push the member/element count.
+ *stack_.template Push<SizeType>(1) = 0;
+ // Call handler
+ bool hr = (dst == IterativeParsingObjectInitialState) ? handler.StartObject() : handler.StartArray();
+ // On handler short circuits the parsing.
+ if (!hr) {
+ RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell());
+ return IterativeParsingErrorState;
+ }
+ else {
+ is.Take();
+ return dst;
+ }
+ }
+
+ case IterativeParsingMemberKeyState:
+ ParseString<parseFlags>(is, handler, true);
+ if (HasParseError())
+ return IterativeParsingErrorState;
+ else
+ return dst;
+
+ case IterativeParsingKeyValueDelimiterState:
+ RAPIDJSON_ASSERT(token == ColonToken);
+ is.Take();
+ return dst;
+
+ case IterativeParsingMemberValueState:
+ // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state.
+ ParseValue<parseFlags>(is, handler);
+ if (HasParseError()) {
+ return IterativeParsingErrorState;
+ }
+ return dst;
+
+ case IterativeParsingElementState:
+ // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state.
+ ParseValue<parseFlags>(is, handler);
+ if (HasParseError()) {
+ return IterativeParsingErrorState;
+ }
+ return dst;
+
+ case IterativeParsingMemberDelimiterState:
+ case IterativeParsingElementDelimiterState:
+ is.Take();
+ // Update member/element count.
+ *stack_.template Top<SizeType>() = *stack_.template Top<SizeType>() + 1;
+ return dst;
+
+ case IterativeParsingObjectFinishState:
+ {
+ // Transit from delimiter is only allowed when trailing commas are enabled
+ if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingMemberDelimiterState) {
+ RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorObjectMissName, is.Tell());
+ return IterativeParsingErrorState;
+ }
+ // Get member count.
+ SizeType c = *stack_.template Pop<SizeType>(1);
+ // If the object is not empty, count the last member.
+ if (src == IterativeParsingMemberValueState)
+ ++c;
+ // Restore the state.
+ IterativeParsingState n = static_cast<IterativeParsingState>(*stack_.template Pop<SizeType>(1));
+ // Transit to Finish state if this is the topmost scope.
+ if (n == IterativeParsingStartState)
+ n = IterativeParsingFinishState;
+ // Call handler
+ bool hr = handler.EndObject(c);
+ // On handler short circuits the parsing.
+ if (!hr) {
+ RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell());
+ return IterativeParsingErrorState;
+ }
+ else {
+ is.Take();
+ return n;
+ }
+ }
+
+ case IterativeParsingArrayFinishState:
+ {
+ // Transit from delimiter is only allowed when trailing commas are enabled
+ if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingElementDelimiterState) {
+ RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorValueInvalid, is.Tell());
+ return IterativeParsingErrorState;
+ }
+ // Get element count.
+ SizeType c = *stack_.template Pop<SizeType>(1);
+ // If the array is not empty, count the last element.
+ if (src == IterativeParsingElementState)
+ ++c;
+ // Restore the state.
+ IterativeParsingState n = static_cast<IterativeParsingState>(*stack_.template Pop<SizeType>(1));
+ // Transit to Finish state if this is the topmost scope.
+ if (n == IterativeParsingStartState)
+ n = IterativeParsingFinishState;
+ // Call handler
+ bool hr = handler.EndArray(c);
+ // On handler short circuits the parsing.
+ if (!hr) {
+ RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell());
+ return IterativeParsingErrorState;
+ }
+ else {
+ is.Take();
+ return n;
+ }
+ }
+
+ default:
+ // This branch is for IterativeParsingValueState actually.
+ // Use `default:` rather than
+ // `case IterativeParsingValueState:` is for code coverage.
+
+ // The IterativeParsingStartState is not enumerated in this switch-case.
+ // It is impossible for that case. And it can be caught by following assertion.
+
+ // The IterativeParsingFinishState is not enumerated in this switch-case either.
+ // It is a "derivative" state which cannot triggered from Predict() directly.
+ // Therefore it cannot happen here. And it can be caught by following assertion.
+ RAPIDJSON_ASSERT(dst == IterativeParsingValueState);
+
+ // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state.
+ ParseValue<parseFlags>(is, handler);
+ if (HasParseError()) {
+ return IterativeParsingErrorState;
+ }
+ return IterativeParsingFinishState;
+ }
+ }
+
+ template <typename InputStream>
+ void HandleError(IterativeParsingState src, InputStream& is) {
+ if (HasParseError()) {
+ // Error flag has been set.
+ return;
+ }
+
+ switch (src) {
+ case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); return;
+ case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); return;
+ case IterativeParsingObjectInitialState:
+ case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); return;
+ case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); return;
+ case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); return;
+ case IterativeParsingKeyValueDelimiterState:
+ case IterativeParsingArrayInitialState:
+ case IterativeParsingElementDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); return;
+ default: RAPIDJSON_ASSERT(src == IterativeParsingElementState); RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return;
+ }
+ }
+
+ template <unsigned parseFlags, typename InputStream, typename Handler>
+ ParseResult IterativeParse(InputStream& is, Handler& handler) {
+ parseResult_.Clear();
+ ClearStackOnExit scope(*this);
+ IterativeParsingState state = IterativeParsingStartState;
+
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
+ while (is.Peek() != '\0') {
+ Token t = Tokenize(is.Peek());
+ IterativeParsingState n = Predict(state, t);
+ IterativeParsingState d = Transit<parseFlags>(state, t, n, is, handler);
+
+ if (d == IterativeParsingErrorState) {
+ HandleError(state, is);
+ break;
+ }
+
+ state = d;
+
+ // Do not further consume streams if a root JSON has been parsed.
+ if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState)
+ break;
+
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
+ }
+
+ // Handle the end of file.
+ if (state != IterativeParsingFinishState)
+ HandleError(state, is);
+
+ return parseResult_;
+ }
+
+ static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string.
+ internal::Stack<StackAllocator> stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing.
+ ParseResult parseResult_;
+}; // class GenericReader
+
+//! Reader with UTF8 encoding and default allocator.
+typedef GenericReader<UTF8<>, UTF8<> > Reader;
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_READER_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/schema.h b/ext/librethinkdbxx/src/rapidjson/schema.h
new file mode 100644
index 00000000..b182aa27
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/schema.h
@@ -0,0 +1,2006 @@
+// Tencent is pleased to support the open source community by making RapidJSON available->
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved->
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License-> You may obtain a copy of the License at
+//
+// http://opensource->org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied-> See the License for the
+// specific language governing permissions and limitations under the License->
+
+#ifndef RAPIDJSON_SCHEMA_H_
+#define RAPIDJSON_SCHEMA_H_
+
+#include "document.h"
+#include "pointer.h"
+#include <cmath> // abs, floor
+
+#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
+#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1
+#else
+#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0
+#endif
+
+#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800))
+#define RAPIDJSON_SCHEMA_USE_STDREGEX 1
+#else
+#define RAPIDJSON_SCHEMA_USE_STDREGEX 0
+#endif
+
+#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
+#include "internal/regex.h"
+#elif RAPIDJSON_SCHEMA_USE_STDREGEX
+#include <regex>
+#endif
+
+#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX
+#define RAPIDJSON_SCHEMA_HAS_REGEX 1
+#else
+#define RAPIDJSON_SCHEMA_HAS_REGEX 0
+#endif
+
+#ifndef RAPIDJSON_SCHEMA_VERBOSE
+#define RAPIDJSON_SCHEMA_VERBOSE 0
+#endif
+
+#if RAPIDJSON_SCHEMA_VERBOSE
+#include "stringbuffer.h"
+#endif
+
+RAPIDJSON_DIAG_PUSH
+
+#if defined(__GNUC__)
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_OFF(weak-vtables)
+RAPIDJSON_DIAG_OFF(exit-time-destructors)
+RAPIDJSON_DIAG_OFF(c++98-compat-pedantic)
+RAPIDJSON_DIAG_OFF(variadic-macros)
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// Verbose Utilities
+
+#if RAPIDJSON_SCHEMA_VERBOSE
+
+namespace internal {
+
+inline void PrintInvalidKeyword(const char* keyword) {
+ printf("Fail keyword: %s\n", keyword);
+}
+
+inline void PrintInvalidKeyword(const wchar_t* keyword) {
+ wprintf(L"Fail keyword: %ls\n", keyword);
+}
+
+inline void PrintInvalidDocument(const char* document) {
+ printf("Fail document: %s\n\n", document);
+}
+
+inline void PrintInvalidDocument(const wchar_t* document) {
+ wprintf(L"Fail document: %ls\n\n", document);
+}
+
+inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) {
+ printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d);
+}
+
+inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) {
+ wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d);
+}
+
+} // namespace internal
+
+#endif // RAPIDJSON_SCHEMA_VERBOSE
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_INVALID_KEYWORD_RETURN
+
+#if RAPIDJSON_SCHEMA_VERBOSE
+#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword)
+#else
+#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword)
+#endif
+
+#define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\
+RAPIDJSON_MULTILINEMACRO_BEGIN\
+ context.invalidKeyword = keyword.GetString();\
+ RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\
+ return false;\
+RAPIDJSON_MULTILINEMACRO_END
+
+///////////////////////////////////////////////////////////////////////////////
+// Forward declarations
+
+template <typename ValueType, typename Allocator>
+class GenericSchemaDocument;
+
+namespace internal {
+
+template <typename SchemaDocumentType>
+class Schema;
+
+///////////////////////////////////////////////////////////////////////////////
+// ISchemaValidator
+
+class ISchemaValidator {
+public:
+ virtual ~ISchemaValidator() {}
+ virtual bool IsValid() const = 0;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// ISchemaStateFactory
+
+template <typename SchemaType>
+class ISchemaStateFactory {
+public:
+ virtual ~ISchemaStateFactory() {}
+ virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0;
+ virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0;
+ virtual void* CreateHasher() = 0;
+ virtual uint64_t GetHashCode(void* hasher) = 0;
+ virtual void DestroryHasher(void* hasher) = 0;
+ virtual void* MallocState(size_t size) = 0;
+ virtual void FreeState(void* p) = 0;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Hasher
+
+// For comparison of compound value
+template<typename Encoding, typename Allocator>
+class Hasher {
+public:
+ typedef typename Encoding::Ch Ch;
+
+ Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {}
+
+ bool Null() { return WriteType(kNullType); }
+ bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); }
+ bool Int(int i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
+ bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
+ bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
+ bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
+ bool Double(double d) {
+ Number n;
+ if (d < 0) n.u.i = static_cast<int64_t>(d);
+ else n.u.u = static_cast<uint64_t>(d);
+ n.d = d;
+ return WriteNumber(n);
+ }
+
+ bool RawNumber(const Ch* str, SizeType len, bool) {
+ WriteBuffer(kNumberType, str, len * sizeof(Ch));
+ return true;
+ }
+
+ bool String(const Ch* str, SizeType len, bool) {
+ WriteBuffer(kStringType, str, len * sizeof(Ch));
+ return true;
+ }
+
+ bool StartObject() { return true; }
+ bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); }
+ bool EndObject(SizeType memberCount) {
+ uint64_t h = Hash(0, kObjectType);
+ uint64_t* kv = stack_.template Pop<uint64_t>(memberCount * 2);
+ for (SizeType i = 0; i < memberCount; i++)
+ h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive
+ *stack_.template Push<uint64_t>() = h;
+ return true;
+ }
+
+ bool StartArray() { return true; }
+ bool EndArray(SizeType elementCount) {
+ uint64_t h = Hash(0, kArrayType);
+ uint64_t* e = stack_.template Pop<uint64_t>(elementCount);
+ for (SizeType i = 0; i < elementCount; i++)
+ h = Hash(h, e[i]); // Use hash to achieve element order sensitive
+ *stack_.template Push<uint64_t>() = h;
+ return true;
+ }
+
+ bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); }
+
+ uint64_t GetHashCode() const {
+ RAPIDJSON_ASSERT(IsValid());
+ return *stack_.template Top<uint64_t>();
+ }
+
+private:
+ static const size_t kDefaultSize = 256;
+ struct Number {
+ union U {
+ uint64_t u;
+ int64_t i;
+ }u;
+ double d;
+ };
+
+ bool WriteType(Type type) { return WriteBuffer(type, 0, 0); }
+
+ bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); }
+
+ bool WriteBuffer(Type type, const void* data, size_t len) {
+ // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/
+ uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type);
+ const unsigned char* d = static_cast<const unsigned char*>(data);
+ for (size_t i = 0; i < len; i++)
+ h = Hash(h, d[i]);
+ *stack_.template Push<uint64_t>() = h;
+ return true;
+ }
+
+ static uint64_t Hash(uint64_t h, uint64_t d) {
+ static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3);
+ h ^= d;
+ h *= kPrime;
+ return h;
+ }
+
+ Stack<Allocator> stack_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// SchemaValidationContext
+
+template <typename SchemaDocumentType>
+struct SchemaValidationContext {
+ typedef Schema<SchemaDocumentType> SchemaType;
+ typedef ISchemaStateFactory<SchemaType> SchemaValidatorFactoryType;
+ typedef typename SchemaType::ValueType ValueType;
+ typedef typename ValueType::Ch Ch;
+
+ enum PatternValidatorType {
+ kPatternValidatorOnly,
+ kPatternValidatorWithProperty,
+ kPatternValidatorWithAdditionalProperty
+ };
+
+ SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) :
+ factory(f),
+ schema(s),
+ valueSchema(),
+ invalidKeyword(),
+ hasher(),
+ arrayElementHashCodes(),
+ validators(),
+ validatorCount(),
+ patternPropertiesValidators(),
+ patternPropertiesValidatorCount(),
+ patternPropertiesSchemas(),
+ patternPropertiesSchemaCount(),
+ valuePatternValidatorType(kPatternValidatorOnly),
+ propertyExist(),
+ inArray(false),
+ valueUniqueness(false),
+ arrayUniqueness(false)
+ {
+ }
+
+ ~SchemaValidationContext() {
+ if (hasher)
+ factory.DestroryHasher(hasher);
+ if (validators) {
+ for (SizeType i = 0; i < validatorCount; i++)
+ factory.DestroySchemaValidator(validators[i]);
+ factory.FreeState(validators);
+ }
+ if (patternPropertiesValidators) {
+ for (SizeType i = 0; i < patternPropertiesValidatorCount; i++)
+ factory.DestroySchemaValidator(patternPropertiesValidators[i]);
+ factory.FreeState(patternPropertiesValidators);
+ }
+ if (patternPropertiesSchemas)
+ factory.FreeState(patternPropertiesSchemas);
+ if (propertyExist)
+ factory.FreeState(propertyExist);
+ }
+
+ SchemaValidatorFactoryType& factory;
+ const SchemaType* schema;
+ const SchemaType* valueSchema;
+ const Ch* invalidKeyword;
+ void* hasher; // Only validator access
+ void* arrayElementHashCodes; // Only validator access this
+ ISchemaValidator** validators;
+ SizeType validatorCount;
+ ISchemaValidator** patternPropertiesValidators;
+ SizeType patternPropertiesValidatorCount;
+ const SchemaType** patternPropertiesSchemas;
+ SizeType patternPropertiesSchemaCount;
+ PatternValidatorType valuePatternValidatorType;
+ PatternValidatorType objectPatternValidatorType;
+ SizeType arrayElementIndex;
+ bool* propertyExist;
+ bool inArray;
+ bool valueUniqueness;
+ bool arrayUniqueness;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Schema
+
+template <typename SchemaDocumentType>
+class Schema {
+public:
+ typedef typename SchemaDocumentType::ValueType ValueType;
+ typedef typename SchemaDocumentType::AllocatorType AllocatorType;
+ typedef typename SchemaDocumentType::PointerType PointerType;
+ typedef typename ValueType::EncodingType EncodingType;
+ typedef typename EncodingType::Ch Ch;
+ typedef SchemaValidationContext<SchemaDocumentType> Context;
+ typedef Schema<SchemaDocumentType> SchemaType;
+ typedef GenericValue<EncodingType, AllocatorType> SValue;
+ friend class GenericSchemaDocument<ValueType, AllocatorType>;
+
+ Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) :
+ allocator_(allocator),
+ enum_(),
+ enumCount_(),
+ not_(),
+ type_((1 << kTotalSchemaType) - 1), // typeless
+ validatorCount_(),
+ properties_(),
+ additionalPropertiesSchema_(),
+ patternProperties_(),
+ patternPropertyCount_(),
+ propertyCount_(),
+ minProperties_(),
+ maxProperties_(SizeType(~0)),
+ additionalProperties_(true),
+ hasDependencies_(),
+ hasRequired_(),
+ hasSchemaDependencies_(),
+ additionalItemsSchema_(),
+ itemsList_(),
+ itemsTuple_(),
+ itemsTupleCount_(),
+ minItems_(),
+ maxItems_(SizeType(~0)),
+ additionalItems_(true),
+ uniqueItems_(false),
+ pattern_(),
+ minLength_(0),
+ maxLength_(~SizeType(0)),
+ exclusiveMinimum_(false),
+ exclusiveMaximum_(false)
+ {
+ typedef typename SchemaDocumentType::ValueType ValueType;
+ typedef typename ValueType::ConstValueIterator ConstValueIterator;
+ typedef typename ValueType::ConstMemberIterator ConstMemberIterator;
+
+ if (!value.IsObject())
+ return;
+
+ if (const ValueType* v = GetMember(value, GetTypeString())) {
+ type_ = 0;
+ if (v->IsString())
+ AddType(*v);
+ else if (v->IsArray())
+ for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr)
+ AddType(*itr);
+ }
+
+ if (const ValueType* v = GetMember(value, GetEnumString()))
+ if (v->IsArray() && v->Size() > 0) {
+ enum_ = static_cast<uint64_t*>(allocator_->Malloc(sizeof(uint64_t) * v->Size()));
+ for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) {
+ typedef Hasher<EncodingType, MemoryPoolAllocator<> > EnumHasherType;
+ char buffer[256 + 24];
+ MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer));
+ EnumHasherType h(&hasherAllocator, 256);
+ itr->Accept(h);
+ enum_[enumCount_++] = h.GetHashCode();
+ }
+ }
+
+ if (schemaDocument) {
+ AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document);
+ AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document);
+ AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document);
+ }
+
+ if (const ValueType* v = GetMember(value, GetNotString())) {
+ schemaDocument->CreateSchema(&not_, p.Append(GetNotString(), allocator_), *v, document);
+ notValidatorIndex_ = validatorCount_;
+ validatorCount_++;
+ }
+
+ // Object
+
+ const ValueType* properties = GetMember(value, GetPropertiesString());
+ const ValueType* required = GetMember(value, GetRequiredString());
+ const ValueType* dependencies = GetMember(value, GetDependenciesString());
+ {
+ // Gather properties from properties/required/dependencies
+ SValue allProperties(kArrayType);
+
+ if (properties && properties->IsObject())
+ for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr)
+ AddUniqueElement(allProperties, itr->name);
+
+ if (required && required->IsArray())
+ for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
+ if (itr->IsString())
+ AddUniqueElement(allProperties, *itr);
+
+ if (dependencies && dependencies->IsObject())
+ for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
+ AddUniqueElement(allProperties, itr->name);
+ if (itr->value.IsArray())
+ for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i)
+ if (i->IsString())
+ AddUniqueElement(allProperties, *i);
+ }
+
+ if (allProperties.Size() > 0) {
+ propertyCount_ = allProperties.Size();
+ properties_ = static_cast<Property*>(allocator_->Malloc(sizeof(Property) * propertyCount_));
+ for (SizeType i = 0; i < propertyCount_; i++) {
+ new (&properties_[i]) Property();
+ properties_[i].name = allProperties[i];
+ properties_[i].schema = GetTypeless();
+ }
+ }
+ }
+
+ if (properties && properties->IsObject()) {
+ PointerType q = p.Append(GetPropertiesString(), allocator_);
+ for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) {
+ SizeType index;
+ if (FindPropertyIndex(itr->name, &index))
+ schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document);
+ }
+ }
+
+ if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) {
+ PointerType q = p.Append(GetPatternPropertiesString(), allocator_);
+ patternProperties_ = static_cast<PatternProperty*>(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount()));
+ patternPropertyCount_ = 0;
+
+ for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) {
+ new (&patternProperties_[patternPropertyCount_]) PatternProperty();
+ patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name);
+ schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document);
+ patternPropertyCount_++;
+ }
+ }
+
+ if (required && required->IsArray())
+ for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
+ if (itr->IsString()) {
+ SizeType index;
+ if (FindPropertyIndex(*itr, &index)) {
+ properties_[index].required = true;
+ hasRequired_ = true;
+ }
+ }
+
+ if (dependencies && dependencies->IsObject()) {
+ PointerType q = p.Append(GetDependenciesString(), allocator_);
+ hasDependencies_ = true;
+ for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
+ SizeType sourceIndex;
+ if (FindPropertyIndex(itr->name, &sourceIndex)) {
+ if (itr->value.IsArray()) {
+ properties_[sourceIndex].dependencies = static_cast<bool*>(allocator_->Malloc(sizeof(bool) * propertyCount_));
+ std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_);
+ for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) {
+ SizeType targetIndex;
+ if (FindPropertyIndex(*targetItr, &targetIndex))
+ properties_[sourceIndex].dependencies[targetIndex] = true;
+ }
+ }
+ else if (itr->value.IsObject()) {
+ hasSchemaDependencies_ = true;
+ schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document);
+ properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_;
+ validatorCount_++;
+ }
+ }
+ }
+ }
+
+ if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) {
+ if (v->IsBool())
+ additionalProperties_ = v->GetBool();
+ else if (v->IsObject())
+ schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document);
+ }
+
+ AssignIfExist(minProperties_, value, GetMinPropertiesString());
+ AssignIfExist(maxProperties_, value, GetMaxPropertiesString());
+
+ // Array
+ if (const ValueType* v = GetMember(value, GetItemsString())) {
+ PointerType q = p.Append(GetItemsString(), allocator_);
+ if (v->IsObject()) // List validation
+ schemaDocument->CreateSchema(&itemsList_, q, *v, document);
+ else if (v->IsArray()) { // Tuple validation
+ itemsTuple_ = static_cast<const Schema**>(allocator_->Malloc(sizeof(const Schema*) * v->Size()));
+ SizeType index = 0;
+ for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++)
+ schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document);
+ }
+ }
+
+ AssignIfExist(minItems_, value, GetMinItemsString());
+ AssignIfExist(maxItems_, value, GetMaxItemsString());
+
+ if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) {
+ if (v->IsBool())
+ additionalItems_ = v->GetBool();
+ else if (v->IsObject())
+ schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document);
+ }
+
+ AssignIfExist(uniqueItems_, value, GetUniqueItemsString());
+
+ // String
+ AssignIfExist(minLength_, value, GetMinLengthString());
+ AssignIfExist(maxLength_, value, GetMaxLengthString());
+
+ if (const ValueType* v = GetMember(value, GetPatternString()))
+ pattern_ = CreatePattern(*v);
+
+ // Number
+ if (const ValueType* v = GetMember(value, GetMinimumString()))
+ if (v->IsNumber())
+ minimum_.CopyFrom(*v, *allocator_);
+
+ if (const ValueType* v = GetMember(value, GetMaximumString()))
+ if (v->IsNumber())
+ maximum_.CopyFrom(*v, *allocator_);
+
+ AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString());
+ AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString());
+
+ if (const ValueType* v = GetMember(value, GetMultipleOfString()))
+ if (v->IsNumber() && v->GetDouble() > 0.0)
+ multipleOf_.CopyFrom(*v, *allocator_);
+ }
+
+ ~Schema() {
+ if (allocator_) {
+ allocator_->Free(enum_);
+ }
+ if (properties_) {
+ for (SizeType i = 0; i < propertyCount_; i++)
+ properties_[i].~Property();
+ AllocatorType::Free(properties_);
+ }
+ if (patternProperties_) {
+ for (SizeType i = 0; i < patternPropertyCount_; i++)
+ patternProperties_[i].~PatternProperty();
+ AllocatorType::Free(patternProperties_);
+ }
+ AllocatorType::Free(itemsTuple_);
+#if RAPIDJSON_SCHEMA_HAS_REGEX
+ if (pattern_) {
+ pattern_->~RegexType();
+ allocator_->Free(pattern_);
+ }
+#endif
+ }
+
+ bool BeginValue(Context& context) const {
+ if (context.inArray) {
+ if (uniqueItems_)
+ context.valueUniqueness = true;
+
+ if (itemsList_)
+ context.valueSchema = itemsList_;
+ else if (itemsTuple_) {
+ if (context.arrayElementIndex < itemsTupleCount_)
+ context.valueSchema = itemsTuple_[context.arrayElementIndex];
+ else if (additionalItemsSchema_)
+ context.valueSchema = additionalItemsSchema_;
+ else if (additionalItems_)
+ context.valueSchema = GetTypeless();
+ else
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString());
+ }
+ else
+ context.valueSchema = GetTypeless();
+
+ context.arrayElementIndex++;
+ }
+ return true;
+ }
+
+ RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const {
+ if (context.patternPropertiesValidatorCount > 0) {
+ bool otherValid = false;
+ SizeType count = context.patternPropertiesValidatorCount;
+ if (context.objectPatternValidatorType != Context::kPatternValidatorOnly)
+ otherValid = context.patternPropertiesValidators[--count]->IsValid();
+
+ bool patternValid = true;
+ for (SizeType i = 0; i < count; i++)
+ if (!context.patternPropertiesValidators[i]->IsValid()) {
+ patternValid = false;
+ break;
+ }
+
+ if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) {
+ if (!patternValid)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
+ }
+ else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) {
+ if (!patternValid || !otherValid)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
+ }
+ else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
+ }
+
+ if (enum_) {
+ const uint64_t h = context.factory.GetHashCode(context.hasher);
+ for (SizeType i = 0; i < enumCount_; i++)
+ if (enum_[i] == h)
+ goto foundEnum;
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString());
+ foundEnum:;
+ }
+
+ if (allOf_.schemas)
+ for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
+ if (!context.validators[i]->IsValid())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString());
+
+ if (anyOf_.schemas) {
+ for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
+ if (context.validators[i]->IsValid())
+ goto foundAny;
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString());
+ foundAny:;
+ }
+
+ if (oneOf_.schemas) {
+ bool oneValid = false;
+ for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
+ if (context.validators[i]->IsValid()) {
+ if (oneValid)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
+ else
+ oneValid = true;
+ }
+ if (!oneValid)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
+ }
+
+ if (not_ && context.validators[notValidatorIndex_]->IsValid())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString());
+
+ return true;
+ }
+
+ bool Null(Context& context) const {
+ if (!(type_ & (1 << kNullSchemaType)))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+ return CreateParallelValidator(context);
+ }
+
+ bool Bool(Context& context, bool) const {
+ if (!(type_ & (1 << kBooleanSchemaType)))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+ return CreateParallelValidator(context);
+ }
+
+ bool Int(Context& context, int i) const {
+ if (!CheckInt(context, i))
+ return false;
+ return CreateParallelValidator(context);
+ }
+
+ bool Uint(Context& context, unsigned u) const {
+ if (!CheckUint(context, u))
+ return false;
+ return CreateParallelValidator(context);
+ }
+
+ bool Int64(Context& context, int64_t i) const {
+ if (!CheckInt(context, i))
+ return false;
+ return CreateParallelValidator(context);
+ }
+
+ bool Uint64(Context& context, uint64_t u) const {
+ if (!CheckUint(context, u))
+ return false;
+ return CreateParallelValidator(context);
+ }
+
+ bool Double(Context& context, double d) const {
+ if (!(type_ & (1 << kNumberSchemaType)))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+ if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d))
+ return false;
+
+ if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d))
+ return false;
+
+ if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d))
+ return false;
+
+ return CreateParallelValidator(context);
+ }
+
+ bool String(Context& context, const Ch* str, SizeType length, bool) const {
+ if (!(type_ & (1 << kStringSchemaType)))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+ if (minLength_ != 0 || maxLength_ != SizeType(~0)) {
+ SizeType count;
+ if (internal::CountStringCodePoint<EncodingType>(str, length, &count)) {
+ if (count < minLength_)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString());
+ if (count > maxLength_)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString());
+ }
+ }
+
+ if (pattern_ && !IsPatternMatch(pattern_, str, length))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString());
+
+ return CreateParallelValidator(context);
+ }
+
+ bool StartObject(Context& context) const {
+ if (!(type_ & (1 << kObjectSchemaType)))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+ if (hasDependencies_ || hasRequired_) {
+ context.propertyExist = static_cast<bool*>(context.factory.MallocState(sizeof(bool) * propertyCount_));
+ std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_);
+ }
+
+ if (patternProperties_) { // pre-allocate schema array
+ SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType
+ context.patternPropertiesSchemas = static_cast<const SchemaType**>(context.factory.MallocState(sizeof(const SchemaType*) * count));
+ context.patternPropertiesSchemaCount = 0;
+ std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count);
+ }
+
+ return CreateParallelValidator(context);
+ }
+
+ bool Key(Context& context, const Ch* str, SizeType len, bool) const {
+ if (patternProperties_) {
+ context.patternPropertiesSchemaCount = 0;
+ for (SizeType i = 0; i < patternPropertyCount_; i++)
+ if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len))
+ context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema;
+ }
+
+ SizeType index;
+ if (FindPropertyIndex(ValueType(str, len).Move(), &index)) {
+ if (context.patternPropertiesSchemaCount > 0) {
+ context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema;
+ context.valueSchema = GetTypeless();
+ context.valuePatternValidatorType = Context::kPatternValidatorWithProperty;
+ }
+ else
+ context.valueSchema = properties_[index].schema;
+
+ if (context.propertyExist)
+ context.propertyExist[index] = true;
+
+ return true;
+ }
+
+ if (additionalPropertiesSchema_) {
+ if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) {
+ context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_;
+ context.valueSchema = GetTypeless();
+ context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty;
+ }
+ else
+ context.valueSchema = additionalPropertiesSchema_;
+ return true;
+ }
+ else if (additionalProperties_) {
+ context.valueSchema = GetTypeless();
+ return true;
+ }
+
+ if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString());
+
+ return true;
+ }
+
+ bool EndObject(Context& context, SizeType memberCount) const {
+ if (hasRequired_)
+ for (SizeType index = 0; index < propertyCount_; index++)
+ if (properties_[index].required)
+ if (!context.propertyExist[index])
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString());
+
+ if (memberCount < minProperties_)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString());
+
+ if (memberCount > maxProperties_)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString());
+
+ if (hasDependencies_) {
+ for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++)
+ if (context.propertyExist[sourceIndex]) {
+ if (properties_[sourceIndex].dependencies) {
+ for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++)
+ if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex])
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
+ }
+ else if (properties_[sourceIndex].dependenciesSchema)
+ if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
+ }
+ }
+
+ return true;
+ }
+
+ bool StartArray(Context& context) const {
+ if (!(type_ & (1 << kArraySchemaType)))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+ context.arrayElementIndex = 0;
+ context.inArray = true;
+
+ return CreateParallelValidator(context);
+ }
+
+ bool EndArray(Context& context, SizeType elementCount) const {
+ context.inArray = false;
+
+ if (elementCount < minItems_)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString());
+
+ if (elementCount > maxItems_)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString());
+
+ return true;
+ }
+
+ // Generate functions for string literal according to Ch
+#define RAPIDJSON_STRING_(name, ...) \
+ static const ValueType& Get##name##String() {\
+ static const Ch s[] = { __VA_ARGS__, '\0' };\
+ static const ValueType v(s, sizeof(s) / sizeof(Ch) - 1);\
+ return v;\
+ }
+
+ RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l')
+ RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n')
+ RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't')
+ RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y')
+ RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g')
+ RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r')
+ RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r')
+ RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e')
+ RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm')
+ RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f')
+ RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f')
+ RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f')
+ RAPIDJSON_STRING_(Not, 'n', 'o', 't')
+ RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
+ RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd')
+ RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's')
+ RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
+ RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
+ RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
+ RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
+ RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's')
+ RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's')
+ RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's')
+ RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's')
+ RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's')
+ RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h')
+ RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h')
+ RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n')
+ RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm')
+ RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm')
+ RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm')
+ RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm')
+ RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f')
+
+#undef RAPIDJSON_STRING_
+
+private:
+ enum SchemaValueType {
+ kNullSchemaType,
+ kBooleanSchemaType,
+ kObjectSchemaType,
+ kArraySchemaType,
+ kStringSchemaType,
+ kNumberSchemaType,
+ kIntegerSchemaType,
+ kTotalSchemaType
+ };
+
+#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
+ typedef internal::GenericRegex<EncodingType> RegexType;
+#elif RAPIDJSON_SCHEMA_USE_STDREGEX
+ typedef std::basic_regex<Ch> RegexType;
+#else
+ typedef char RegexType;
+#endif
+
+ struct SchemaArray {
+ SchemaArray() : schemas(), count() {}
+ ~SchemaArray() { AllocatorType::Free(schemas); }
+ const SchemaType** schemas;
+ SizeType begin; // begin index of context.validators
+ SizeType count;
+ };
+
+ static const SchemaType* GetTypeless() {
+ static SchemaType typeless(0, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0);
+ return &typeless;
+ }
+
+ template <typename V1, typename V2>
+ void AddUniqueElement(V1& a, const V2& v) {
+ for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
+ if (*itr == v)
+ return;
+ V1 c(v, *allocator_);
+ a.PushBack(c, *allocator_);
+ }
+
+ static const ValueType* GetMember(const ValueType& value, const ValueType& name) {
+ typename ValueType::ConstMemberIterator itr = value.FindMember(name);
+ return itr != value.MemberEnd() ? &(itr->value) : 0;
+ }
+
+ static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) {
+ if (const ValueType* v = GetMember(value, name))
+ if (v->IsBool())
+ out = v->GetBool();
+ }
+
+ static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) {
+ if (const ValueType* v = GetMember(value, name))
+ if (v->IsUint64() && v->GetUint64() <= SizeType(~0))
+ out = static_cast<SizeType>(v->GetUint64());
+ }
+
+ void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) {
+ if (const ValueType* v = GetMember(value, name)) {
+ if (v->IsArray() && v->Size() > 0) {
+ PointerType q = p.Append(name, allocator_);
+ out.count = v->Size();
+ out.schemas = static_cast<const Schema**>(allocator_->Malloc(out.count * sizeof(const Schema*)));
+ memset(out.schemas, 0, sizeof(Schema*)* out.count);
+ for (SizeType i = 0; i < out.count; i++)
+ schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document);
+ out.begin = validatorCount_;
+ validatorCount_ += out.count;
+ }
+ }
+ }
+
+#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
+ template <typename ValueType>
+ RegexType* CreatePattern(const ValueType& value) {
+ if (value.IsString()) {
+ RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString());
+ if (!r->IsValid()) {
+ r->~RegexType();
+ AllocatorType::Free(r);
+ r = 0;
+ }
+ return r;
+ }
+ return 0;
+ }
+
+ static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) {
+ return pattern->Search(str);
+ }
+#elif RAPIDJSON_SCHEMA_USE_STDREGEX
+ template <typename ValueType>
+ RegexType* CreatePattern(const ValueType& value) {
+ if (value.IsString())
+ try {
+ return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript);
+ }
+ catch (const std::regex_error&) {
+ }
+ return 0;
+ }
+
+ static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) {
+ std::match_results<const Ch*> r;
+ return std::regex_search(str, str + length, r, *pattern);
+ }
+#else
+ template <typename ValueType>
+ RegexType* CreatePattern(const ValueType&) { return 0; }
+
+ static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; }
+#endif // RAPIDJSON_SCHEMA_USE_STDREGEX
+
+ void AddType(const ValueType& type) {
+ if (type == GetNullString() ) type_ |= 1 << kNullSchemaType;
+ else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType;
+ else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType;
+ else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType;
+ else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType;
+ else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType;
+ else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType);
+ }
+
+ bool CreateParallelValidator(Context& context) const {
+ if (enum_ || context.arrayUniqueness)
+ context.hasher = context.factory.CreateHasher();
+
+ if (validatorCount_) {
+ RAPIDJSON_ASSERT(context.validators == 0);
+ context.validators = static_cast<ISchemaValidator**>(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_));
+ context.validatorCount = validatorCount_;
+
+ if (allOf_.schemas)
+ CreateSchemaValidators(context, allOf_);
+
+ if (anyOf_.schemas)
+ CreateSchemaValidators(context, anyOf_);
+
+ if (oneOf_.schemas)
+ CreateSchemaValidators(context, oneOf_);
+
+ if (not_)
+ context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_);
+
+ if (hasSchemaDependencies_) {
+ for (SizeType i = 0; i < propertyCount_; i++)
+ if (properties_[i].dependenciesSchema)
+ context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema);
+ }
+ }
+
+ return true;
+ }
+
+ void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const {
+ for (SizeType i = 0; i < schemas.count; i++)
+ context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]);
+ }
+
+ // O(n)
+ bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const {
+ SizeType len = name.GetStringLength();
+ const Ch* str = name.GetString();
+ for (SizeType index = 0; index < propertyCount_; index++)
+ if (properties_[index].name.GetStringLength() == len &&
+ (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0))
+ {
+ *outIndex = index;
+ return true;
+ }
+ return false;
+ }
+
+ bool CheckInt(Context& context, int64_t i) const {
+ if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+ if (!minimum_.IsNull()) {
+ if (minimum_.IsInt64()) {
+ if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
+ }
+ else if (minimum_.IsUint64()) {
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64()
+ }
+ else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
+ return false;
+ }
+
+ if (!maximum_.IsNull()) {
+ if (maximum_.IsInt64()) {
+ if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
+ }
+ else if (maximum_.IsUint64())
+ /* do nothing */; // i <= max(int64_t) < maximum_.GetUint64()
+ else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
+ return false;
+ }
+
+ if (!multipleOf_.IsNull()) {
+ if (multipleOf_.IsUint64()) {
+ if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
+ }
+ else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
+ return false;
+ }
+
+ return true;
+ }
+
+ bool CheckUint(Context& context, uint64_t i) const {
+ if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+ if (!minimum_.IsNull()) {
+ if (minimum_.IsUint64()) {
+ if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
+ }
+ else if (minimum_.IsInt64())
+ /* do nothing */; // i >= 0 > minimum.Getint64()
+ else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
+ return false;
+ }
+
+ if (!maximum_.IsNull()) {
+ if (maximum_.IsUint64()) {
+ if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
+ }
+ else if (maximum_.IsInt64())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_
+ else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
+ return false;
+ }
+
+ if (!multipleOf_.IsNull()) {
+ if (multipleOf_.IsUint64()) {
+ if (i % multipleOf_.GetUint64() != 0)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
+ }
+ else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
+ return false;
+ }
+
+ return true;
+ }
+
+ bool CheckDoubleMinimum(Context& context, double d) const {
+ if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
+ return true;
+ }
+
+ bool CheckDoubleMaximum(Context& context, double d) const {
+ if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
+ return true;
+ }
+
+ bool CheckDoubleMultipleOf(Context& context, double d) const {
+ double a = std::abs(d), b = std::abs(multipleOf_.GetDouble());
+ double q = std::floor(a / b);
+ double r = a - q * b;
+ if (r > 0.0)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
+ return true;
+ }
+
+ struct Property {
+ Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {}
+ ~Property() { AllocatorType::Free(dependencies); }
+ SValue name;
+ const SchemaType* schema;
+ const SchemaType* dependenciesSchema;
+ SizeType dependenciesValidatorIndex;
+ bool* dependencies;
+ bool required;
+ };
+
+ struct PatternProperty {
+ PatternProperty() : schema(), pattern() {}
+ ~PatternProperty() {
+ if (pattern) {
+ pattern->~RegexType();
+ AllocatorType::Free(pattern);
+ }
+ }
+ const SchemaType* schema;
+ RegexType* pattern;
+ };
+
+ AllocatorType* allocator_;
+ uint64_t* enum_;
+ SizeType enumCount_;
+ SchemaArray allOf_;
+ SchemaArray anyOf_;
+ SchemaArray oneOf_;
+ const SchemaType* not_;
+ unsigned type_; // bitmask of kSchemaType
+ SizeType validatorCount_;
+ SizeType notValidatorIndex_;
+
+ Property* properties_;
+ const SchemaType* additionalPropertiesSchema_;
+ PatternProperty* patternProperties_;
+ SizeType patternPropertyCount_;
+ SizeType propertyCount_;
+ SizeType minProperties_;
+ SizeType maxProperties_;
+ bool additionalProperties_;
+ bool hasDependencies_;
+ bool hasRequired_;
+ bool hasSchemaDependencies_;
+
+ const SchemaType* additionalItemsSchema_;
+ const SchemaType* itemsList_;
+ const SchemaType** itemsTuple_;
+ SizeType itemsTupleCount_;
+ SizeType minItems_;
+ SizeType maxItems_;
+ bool additionalItems_;
+ bool uniqueItems_;
+
+ RegexType* pattern_;
+ SizeType minLength_;
+ SizeType maxLength_;
+
+ SValue minimum_;
+ SValue maximum_;
+ SValue multipleOf_;
+ bool exclusiveMinimum_;
+ bool exclusiveMaximum_;
+};
+
+template<typename Stack, typename Ch>
+struct TokenHelper {
+ RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
+ *documentStack.template Push<Ch>() = '/';
+ char buffer[21];
+ size_t length = static_cast<size_t>((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer);
+ for (size_t i = 0; i < length; i++)
+ *documentStack.template Push<Ch>() = buffer[i];
+ }
+};
+
+// Partial specialized version for char to prevent buffer copying.
+template <typename Stack>
+struct TokenHelper<Stack, char> {
+ RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
+ if (sizeof(SizeType) == 4) {
+ char *buffer = documentStack.template Push<char>(1 + 10); // '/' + uint
+ *buffer++ = '/';
+ const char* end = internal::u32toa(index, buffer);
+ documentStack.template Pop<char>(static_cast<size_t>(10 - (end - buffer)));
+ }
+ else {
+ char *buffer = documentStack.template Push<char>(1 + 20); // '/' + uint64
+ *buffer++ = '/';
+ const char* end = internal::u64toa(index, buffer);
+ documentStack.template Pop<char>(static_cast<size_t>(20 - (end - buffer)));
+ }
+ }
+};
+
+} // namespace internal
+
+///////////////////////////////////////////////////////////////////////////////
+// IGenericRemoteSchemaDocumentProvider
+
+template <typename SchemaDocumentType>
+class IGenericRemoteSchemaDocumentProvider {
+public:
+ typedef typename SchemaDocumentType::Ch Ch;
+
+ virtual ~IGenericRemoteSchemaDocumentProvider() {}
+ virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericSchemaDocument
+
+//! JSON schema document.
+/*!
+ A JSON schema document is a compiled version of a JSON schema.
+ It is basically a tree of internal::Schema.
+
+ \note This is an immutable class (i.e. its instance cannot be modified after construction).
+ \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding.
+ \tparam Allocator Allocator type for allocating memory of this document.
+*/
+template <typename ValueT, typename Allocator = CrtAllocator>
+class GenericSchemaDocument {
+public:
+ typedef ValueT ValueType;
+ typedef IGenericRemoteSchemaDocumentProvider<GenericSchemaDocument> IRemoteSchemaDocumentProviderType;
+ typedef Allocator AllocatorType;
+ typedef typename ValueType::EncodingType EncodingType;
+ typedef typename EncodingType::Ch Ch;
+ typedef internal::Schema<GenericSchemaDocument> SchemaType;
+ typedef GenericPointer<ValueType, Allocator> PointerType;
+ friend class internal::Schema<GenericSchemaDocument>;
+ template <typename, typename, typename>
+ friend class GenericSchemaValidator;
+
+ //! Constructor.
+ /*!
+ Compile a JSON document into schema document.
+
+ \param document A JSON document as source.
+ \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null.
+ \param allocator An optional allocator instance for allocating memory. Can be null.
+ */
+ explicit GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) :
+ remoteProvider_(remoteProvider),
+ allocator_(allocator),
+ ownAllocator_(),
+ root_(),
+ schemaMap_(allocator, kInitialSchemaMapSize),
+ schemaRef_(allocator, kInitialSchemaRefSize)
+ {
+ if (!allocator_)
+ ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
+
+ // Generate root schema, it will call CreateSchema() to create sub-schemas,
+ // And call AddRefSchema() if there are $ref.
+ CreateSchemaRecursive(&root_, PointerType(), document, document);
+
+ // Resolve $ref
+ while (!schemaRef_.Empty()) {
+ SchemaRefEntry* refEntry = schemaRef_.template Pop<SchemaRefEntry>(1);
+ if (const SchemaType* s = GetSchema(refEntry->target)) {
+ if (refEntry->schema)
+ *refEntry->schema = s;
+
+ // Create entry in map if not exist
+ if (!GetSchema(refEntry->source)) {
+ new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(refEntry->source, const_cast<SchemaType*>(s), false, allocator_);
+ }
+ }
+ refEntry->~SchemaRefEntry();
+ }
+
+ RAPIDJSON_ASSERT(root_ != 0);
+
+ schemaRef_.ShrinkToFit(); // Deallocate all memory for ref
+ }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ //! Move constructor in C++11
+ GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT :
+ remoteProvider_(rhs.remoteProvider_),
+ allocator_(rhs.allocator_),
+ ownAllocator_(rhs.ownAllocator_),
+ root_(rhs.root_),
+ schemaMap_(std::move(rhs.schemaMap_)),
+ schemaRef_(std::move(rhs.schemaRef_))
+ {
+ rhs.remoteProvider_ = 0;
+ rhs.allocator_ = 0;
+ rhs.ownAllocator_ = 0;
+ }
+#endif
+
+ //! Destructor
+ ~GenericSchemaDocument() {
+ while (!schemaMap_.Empty())
+ schemaMap_.template Pop<SchemaEntry>(1)->~SchemaEntry();
+
+ RAPIDJSON_DELETE(ownAllocator_);
+ }
+
+ //! Get the root schema.
+ const SchemaType& GetRoot() const { return *root_; }
+
+private:
+ //! Prohibit copying
+ GenericSchemaDocument(const GenericSchemaDocument&);
+ //! Prohibit assignment
+ GenericSchemaDocument& operator=(const GenericSchemaDocument&);
+
+ struct SchemaRefEntry {
+ SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {}
+ PointerType source;
+ PointerType target;
+ const SchemaType** schema;
+ };
+
+ struct SchemaEntry {
+ SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {}
+ ~SchemaEntry() {
+ if (owned) {
+ schema->~SchemaType();
+ Allocator::Free(schema);
+ }
+ }
+ PointerType pointer;
+ SchemaType* schema;
+ bool owned;
+ };
+
+ void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
+ if (schema)
+ *schema = SchemaType::GetTypeless();
+
+ if (v.GetType() == kObjectType) {
+ const SchemaType* s = GetSchema(pointer);
+ if (!s)
+ CreateSchema(schema, pointer, v, document);
+
+ for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
+ CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document);
+ }
+ else if (v.GetType() == kArrayType)
+ for (SizeType i = 0; i < v.Size(); i++)
+ CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document);
+ }
+
+ void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
+ RAPIDJSON_ASSERT(pointer.IsValid());
+ if (v.IsObject()) {
+ if (!HandleRefSchema(pointer, schema, v, document)) {
+ SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_);
+ new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(pointer, s, true, allocator_);
+ if (schema)
+ *schema = s;
+ }
+ }
+ }
+
+ bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) {
+ static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' };
+ static const ValueType kRefValue(kRefString, 4);
+
+ typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue);
+ if (itr == v.MemberEnd())
+ return false;
+
+ if (itr->value.IsString()) {
+ SizeType len = itr->value.GetStringLength();
+ if (len > 0) {
+ const Ch* s = itr->value.GetString();
+ SizeType i = 0;
+ while (i < len && s[i] != '#') // Find the first #
+ i++;
+
+ if (i > 0) { // Remote reference, resolve immediately
+ if (remoteProvider_) {
+ if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i - 1)) {
+ PointerType pointer(&s[i], len - i, allocator_);
+ if (pointer.IsValid()) {
+ if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) {
+ if (schema)
+ *schema = sc;
+ return true;
+ }
+ }
+ }
+ }
+ }
+ else if (s[i] == '#') { // Local reference, defer resolution
+ PointerType pointer(&s[i], len - i, allocator_);
+ if (pointer.IsValid()) {
+ if (const ValueType* nv = pointer.Get(document))
+ if (HandleRefSchema(source, schema, *nv, document))
+ return true;
+
+ new (schemaRef_.template Push<SchemaRefEntry>()) SchemaRefEntry(source, pointer, schema, allocator_);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ const SchemaType* GetSchema(const PointerType& pointer) const {
+ for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
+ if (pointer == target->pointer)
+ return target->schema;
+ return 0;
+ }
+
+ PointerType GetPointer(const SchemaType* schema) const {
+ for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
+ if (schema == target->schema)
+ return target->pointer;
+ return PointerType();
+ }
+
+ static const size_t kInitialSchemaMapSize = 64;
+ static const size_t kInitialSchemaRefSize = 64;
+
+ IRemoteSchemaDocumentProviderType* remoteProvider_;
+ Allocator *allocator_;
+ Allocator *ownAllocator_;
+ const SchemaType* root_; //!< Root schema.
+ internal::Stack<Allocator> schemaMap_; // Stores created Pointer -> Schemas
+ internal::Stack<Allocator> schemaRef_; // Stores Pointer from $ref and schema which holds the $ref
+};
+
+//! GenericSchemaDocument using Value type.
+typedef GenericSchemaDocument<Value> SchemaDocument;
+//! IGenericRemoteSchemaDocumentProvider using SchemaDocument.
+typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericSchemaValidator
+
+//! JSON Schema Validator.
+/*!
+ A SAX style JSON schema validator.
+ It uses a \c GenericSchemaDocument to validate SAX events.
+ It delegates the incoming SAX events to an output handler.
+ The default output handler does nothing.
+ It can be reused multiple times by calling \c Reset().
+
+ \tparam SchemaDocumentType Type of schema document.
+ \tparam OutputHandler Type of output handler. Default handler does nothing.
+ \tparam StateAllocator Allocator for storing the internal validation states.
+*/
+template <
+ typename SchemaDocumentType,
+ typename OutputHandler = BaseReaderHandler<typename SchemaDocumentType::SchemaType::EncodingType>,
+ typename StateAllocator = CrtAllocator>
+class GenericSchemaValidator :
+ public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>,
+ public internal::ISchemaValidator
+{
+public:
+ typedef typename SchemaDocumentType::SchemaType SchemaType;
+ typedef typename SchemaDocumentType::PointerType PointerType;
+ typedef typename SchemaType::EncodingType EncodingType;
+ typedef typename EncodingType::Ch Ch;
+
+ //! Constructor without output handler.
+ /*!
+ \param schemaDocument The schema document to conform to.
+ \param allocator Optional allocator for storing internal validation states.
+ \param schemaStackCapacity Optional initial capacity of schema path stack.
+ \param documentStackCapacity Optional initial capacity of document path stack.
+ */
+ GenericSchemaValidator(
+ const SchemaDocumentType& schemaDocument,
+ StateAllocator* allocator = 0,
+ size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
+ size_t documentStackCapacity = kDefaultDocumentStackCapacity)
+ :
+ schemaDocument_(&schemaDocument),
+ root_(schemaDocument.GetRoot()),
+ outputHandler_(GetNullHandler()),
+ stateAllocator_(allocator),
+ ownStateAllocator_(0),
+ schemaStack_(allocator, schemaStackCapacity),
+ documentStack_(allocator, documentStackCapacity),
+ valid_(true)
+#if RAPIDJSON_SCHEMA_VERBOSE
+ , depth_(0)
+#endif
+ {
+ }
+
+ //! Constructor with output handler.
+ /*!
+ \param schemaDocument The schema document to conform to.
+ \param allocator Optional allocator for storing internal validation states.
+ \param schemaStackCapacity Optional initial capacity of schema path stack.
+ \param documentStackCapacity Optional initial capacity of document path stack.
+ */
+ GenericSchemaValidator(
+ const SchemaDocumentType& schemaDocument,
+ OutputHandler& outputHandler,
+ StateAllocator* allocator = 0,
+ size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
+ size_t documentStackCapacity = kDefaultDocumentStackCapacity)
+ :
+ schemaDocument_(&schemaDocument),
+ root_(schemaDocument.GetRoot()),
+ outputHandler_(outputHandler),
+ stateAllocator_(allocator),
+ ownStateAllocator_(0),
+ schemaStack_(allocator, schemaStackCapacity),
+ documentStack_(allocator, documentStackCapacity),
+ valid_(true)
+#if RAPIDJSON_SCHEMA_VERBOSE
+ , depth_(0)
+#endif
+ {
+ }
+
+ //! Destructor.
+ ~GenericSchemaValidator() {
+ Reset();
+ RAPIDJSON_DELETE(ownStateAllocator_);
+ }
+
+ //! Reset the internal states.
+ void Reset() {
+ while (!schemaStack_.Empty())
+ PopSchema();
+ documentStack_.Clear();
+ valid_ = true;
+ }
+
+ //! Checks whether the current state is valid.
+ // Implementation of ISchemaValidator
+ virtual bool IsValid() const { return valid_; }
+
+ //! Gets the JSON pointer pointed to the invalid schema.
+ PointerType GetInvalidSchemaPointer() const {
+ return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema());
+ }
+
+ //! Gets the keyword of invalid schema.
+ const Ch* GetInvalidSchemaKeyword() const {
+ return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword;
+ }
+
+ //! Gets the JSON pointer pointed to the invalid value.
+ PointerType GetInvalidDocumentPointer() const {
+ return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch));
+ }
+
+#if RAPIDJSON_SCHEMA_VERBOSE
+#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \
+RAPIDJSON_MULTILINEMACRO_BEGIN\
+ *documentStack_.template Push<Ch>() = '\0';\
+ documentStack_.template Pop<Ch>(1);\
+ internal::PrintInvalidDocument(documentStack_.template Bottom<Ch>());\
+RAPIDJSON_MULTILINEMACRO_END
+#else
+#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_()
+#endif
+
+#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
+ if (!valid_) return false; \
+ if (!BeginValue() || !CurrentSchema().method arg1) {\
+ RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\
+ return valid_ = false;\
+ }
+
+#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\
+ for (Context* context = schemaStack_.template Bottom<Context>(); context != schemaStack_.template End<Context>(); context++) {\
+ if (context->hasher)\
+ static_cast<HasherType*>(context->hasher)->method arg2;\
+ if (context->validators)\
+ for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\
+ static_cast<GenericSchemaValidator*>(context->validators[i_])->method arg2;\
+ if (context->patternPropertiesValidators)\
+ for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\
+ static_cast<GenericSchemaValidator*>(context->patternPropertiesValidators[i_])->method arg2;\
+ }
+
+#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\
+ return valid_ = EndValue() && outputHandler_.method arg2
+
+#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \
+ RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\
+ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\
+ RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2)
+
+ bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext() ), ( )); }
+ bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); }
+ bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); }
+ bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); }
+ bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); }
+ bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); }
+ bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); }
+ bool RawNumber(const Ch* str, SizeType length, bool copy)
+ { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
+ bool String(const Ch* str, SizeType length, bool copy)
+ { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
+
+ bool StartObject() {
+ RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext()));
+ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ());
+ return valid_ = outputHandler_.StartObject();
+ }
+
+ bool Key(const Ch* str, SizeType len, bool copy) {
+ if (!valid_) return false;
+ AppendToken(str, len);
+ if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false;
+ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
+ return valid_ = outputHandler_.Key(str, len, copy);
+ }
+
+ bool EndObject(SizeType memberCount) {
+ if (!valid_) return false;
+ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount));
+ if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false;
+ RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount));
+ }
+
+ bool StartArray() {
+ RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext()));
+ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ());
+ return valid_ = outputHandler_.StartArray();
+ }
+
+ bool EndArray(SizeType elementCount) {
+ if (!valid_) return false;
+ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount));
+ if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false;
+ RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount));
+ }
+
+#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_
+#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_
+#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_
+#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_
+
+ // Implementation of ISchemaStateFactory<SchemaType>
+ virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) {
+ return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root,
+#if RAPIDJSON_SCHEMA_VERBOSE
+ depth_ + 1,
+#endif
+ &GetStateAllocator());
+ }
+
+ virtual void DestroySchemaValidator(ISchemaValidator* validator) {
+ GenericSchemaValidator* v = static_cast<GenericSchemaValidator*>(validator);
+ v->~GenericSchemaValidator();
+ StateAllocator::Free(v);
+ }
+
+ virtual void* CreateHasher() {
+ return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator());
+ }
+
+ virtual uint64_t GetHashCode(void* hasher) {
+ return static_cast<HasherType*>(hasher)->GetHashCode();
+ }
+
+ virtual void DestroryHasher(void* hasher) {
+ HasherType* h = static_cast<HasherType*>(hasher);
+ h->~HasherType();
+ StateAllocator::Free(h);
+ }
+
+ virtual void* MallocState(size_t size) {
+ return GetStateAllocator().Malloc(size);
+ }
+
+ virtual void FreeState(void* p) {
+ return StateAllocator::Free(p);
+ }
+
+private:
+ typedef typename SchemaType::Context Context;
+ typedef GenericValue<UTF8<>, StateAllocator> HashCodeArray;
+ typedef internal::Hasher<EncodingType, StateAllocator> HasherType;
+
+ GenericSchemaValidator(
+ const SchemaDocumentType& schemaDocument,
+ const SchemaType& root,
+#if RAPIDJSON_SCHEMA_VERBOSE
+ unsigned depth,
+#endif
+ StateAllocator* allocator = 0,
+ size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
+ size_t documentStackCapacity = kDefaultDocumentStackCapacity)
+ :
+ schemaDocument_(&schemaDocument),
+ root_(root),
+ outputHandler_(GetNullHandler()),
+ stateAllocator_(allocator),
+ ownStateAllocator_(0),
+ schemaStack_(allocator, schemaStackCapacity),
+ documentStack_(allocator, documentStackCapacity),
+ valid_(true)
+#if RAPIDJSON_SCHEMA_VERBOSE
+ , depth_(depth)
+#endif
+ {
+ }
+
+ StateAllocator& GetStateAllocator() {
+ if (!stateAllocator_)
+ stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator());
+ return *stateAllocator_;
+ }
+
+ bool BeginValue() {
+ if (schemaStack_.Empty())
+ PushSchema(root_);
+ else {
+ if (CurrentContext().inArray)
+ internal::TokenHelper<internal::Stack<StateAllocator>, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex);
+
+ if (!CurrentSchema().BeginValue(CurrentContext()))
+ return false;
+
+ SizeType count = CurrentContext().patternPropertiesSchemaCount;
+ const SchemaType** sa = CurrentContext().patternPropertiesSchemas;
+ typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType;
+ bool valueUniqueness = CurrentContext().valueUniqueness;
+ if (CurrentContext().valueSchema)
+ PushSchema(*CurrentContext().valueSchema);
+
+ if (count > 0) {
+ CurrentContext().objectPatternValidatorType = patternValidatorType;
+ ISchemaValidator**& va = CurrentContext().patternPropertiesValidators;
+ SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount;
+ va = static_cast<ISchemaValidator**>(MallocState(sizeof(ISchemaValidator*) * count));
+ for (SizeType i = 0; i < count; i++)
+ va[validatorCount++] = CreateSchemaValidator(*sa[i]);
+ }
+
+ CurrentContext().arrayUniqueness = valueUniqueness;
+ }
+ return true;
+ }
+
+ bool EndValue() {
+ if (!CurrentSchema().EndValue(CurrentContext()))
+ return false;
+
+#if RAPIDJSON_SCHEMA_VERBOSE
+ GenericStringBuffer<EncodingType> sb;
+ schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb);
+
+ *documentStack_.template Push<Ch>() = '\0';
+ documentStack_.template Pop<Ch>(1);
+ internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom<Ch>());
+#endif
+
+ uint64_t h = CurrentContext().arrayUniqueness ? static_cast<HasherType*>(CurrentContext().hasher)->GetHashCode() : 0;
+
+ PopSchema();
+
+ if (!schemaStack_.Empty()) {
+ Context& context = CurrentContext();
+ if (context.valueUniqueness) {
+ HashCodeArray* a = static_cast<HashCodeArray*>(context.arrayElementHashCodes);
+ if (!a)
+ CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType);
+ for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr)
+ if (itr->GetUint64() == h)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString());
+ a->PushBack(h, GetStateAllocator());
+ }
+ }
+
+ // Remove the last token of document pointer
+ while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/')
+ ;
+
+ return true;
+ }
+
+ void AppendToken(const Ch* str, SizeType len) {
+ documentStack_.template Reserve<Ch>(1 + len * 2); // worst case all characters are escaped as two characters
+ *documentStack_.template PushUnsafe<Ch>() = '/';
+ for (SizeType i = 0; i < len; i++) {
+ if (str[i] == '~') {
+ *documentStack_.template PushUnsafe<Ch>() = '~';
+ *documentStack_.template PushUnsafe<Ch>() = '0';
+ }
+ else if (str[i] == '/') {
+ *documentStack_.template PushUnsafe<Ch>() = '~';
+ *documentStack_.template PushUnsafe<Ch>() = '1';
+ }
+ else
+ *documentStack_.template PushUnsafe<Ch>() = str[i];
+ }
+ }
+
+ RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, &schema); }
+
+ RAPIDJSON_FORCEINLINE void PopSchema() {
+ Context* c = schemaStack_.template Pop<Context>(1);
+ if (HashCodeArray* a = static_cast<HashCodeArray*>(c->arrayElementHashCodes)) {
+ a->~HashCodeArray();
+ StateAllocator::Free(a);
+ }
+ c->~Context();
+ }
+
+ const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
+ Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
+ const Context& CurrentContext() const { return *schemaStack_.template Top<Context>(); }
+
+ static OutputHandler& GetNullHandler() {
+ static OutputHandler nullHandler;
+ return nullHandler;
+ }
+
+ static const size_t kDefaultSchemaStackCapacity = 1024;
+ static const size_t kDefaultDocumentStackCapacity = 256;
+ const SchemaDocumentType* schemaDocument_;
+ const SchemaType& root_;
+ OutputHandler& outputHandler_;
+ StateAllocator* stateAllocator_;
+ StateAllocator* ownStateAllocator_;
+ internal::Stack<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *)
+ internal::Stack<StateAllocator> documentStack_; //!< stack to store the current path of validating document (Ch)
+ bool valid_;
+#if RAPIDJSON_SCHEMA_VERBOSE
+ unsigned depth_;
+#endif
+};
+
+typedef GenericSchemaValidator<SchemaDocument> SchemaValidator;
+
+///////////////////////////////////////////////////////////////////////////////
+// SchemaValidatingReader
+
+//! A helper class for parsing with validation.
+/*!
+ This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate().
+
+ \tparam parseFlags Combination of \ref ParseFlag.
+ \tparam InputStream Type of input stream, implementing Stream concept.
+ \tparam SourceEncoding Encoding of the input stream.
+ \tparam SchemaDocumentType Type of schema document.
+ \tparam StackAllocator Allocator type for stack.
+*/
+template <
+ unsigned parseFlags,
+ typename InputStream,
+ typename SourceEncoding,
+ typename SchemaDocumentType = SchemaDocument,
+ typename StackAllocator = CrtAllocator>
+class SchemaValidatingReader {
+public:
+ typedef typename SchemaDocumentType::PointerType PointerType;
+ typedef typename InputStream::Ch Ch;
+
+ //! Constructor
+ /*!
+ \param is Input stream.
+ \param sd Schema document.
+ */
+ SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {}
+
+ template <typename Handler>
+ bool operator()(Handler& handler) {
+ GenericReader<SourceEncoding, typename SchemaDocumentType::EncodingType, StackAllocator> reader;
+ GenericSchemaValidator<SchemaDocumentType, Handler> validator(sd_, handler);
+ parseResult_ = reader.template Parse<parseFlags>(is_, validator);
+
+ isValid_ = validator.IsValid();
+ if (isValid_) {
+ invalidSchemaPointer_ = PointerType();
+ invalidSchemaKeyword_ = 0;
+ invalidDocumentPointer_ = PointerType();
+ }
+ else {
+ invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
+ invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
+ invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
+ }
+
+ return parseResult_;
+ }
+
+ const ParseResult& GetParseResult() const { return parseResult_; }
+ bool IsValid() const { return isValid_; }
+ const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; }
+ const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
+ const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
+
+private:
+ InputStream& is_;
+ const SchemaDocumentType& sd_;
+
+ ParseResult parseResult_;
+ PointerType invalidSchemaPointer_;
+ const Ch* invalidSchemaKeyword_;
+ PointerType invalidDocumentPointer_;
+ bool isValid_;
+};
+
+RAPIDJSON_NAMESPACE_END
+RAPIDJSON_DIAG_POP
+
+#endif // RAPIDJSON_SCHEMA_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/stream.h b/ext/librethinkdbxx/src/rapidjson/stream.h
new file mode 100644
index 00000000..fef82c25
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/stream.h
@@ -0,0 +1,179 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "rapidjson.h"
+
+#ifndef RAPIDJSON_STREAM_H_
+#define RAPIDJSON_STREAM_H_
+
+#include "encodings.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// Stream
+
+/*! \class rapidjson::Stream
+ \brief Concept for reading and writing characters.
+
+ For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd().
+
+ For write-only stream, only need to implement Put() and Flush().
+
+\code
+concept Stream {
+ typename Ch; //!< Character type of the stream.
+
+ //! Read the current character from stream without moving the read cursor.
+ Ch Peek() const;
+
+ //! Read the current character from stream and moving the read cursor to next character.
+ Ch Take();
+
+ //! Get the current read cursor.
+ //! \return Number of characters read from start.
+ size_t Tell();
+
+ //! Begin writing operation at the current read pointer.
+ //! \return The begin writer pointer.
+ Ch* PutBegin();
+
+ //! Write a character.
+ void Put(Ch c);
+
+ //! Flush the buffer.
+ void Flush();
+
+ //! End the writing operation.
+ //! \param begin The begin write pointer returned by PutBegin().
+ //! \return Number of characters written.
+ size_t PutEnd(Ch* begin);
+}
+\endcode
+*/
+
+//! Provides additional information for stream.
+/*!
+ By using traits pattern, this type provides a default configuration for stream.
+ For custom stream, this type can be specialized for other configuration.
+ See TEST(Reader, CustomStringStream) in readertest.cpp for example.
+*/
+template<typename Stream>
+struct StreamTraits {
+ //! Whether to make local copy of stream for optimization during parsing.
+ /*!
+ By default, for safety, streams do not use local copy optimization.
+ Stream that can be copied fast should specialize this, like StreamTraits<StringStream>.
+ */
+ enum { copyOptimization = 0 };
+};
+
+//! Reserve n characters for writing to a stream.
+template<typename Stream>
+inline void PutReserve(Stream& stream, size_t count) {
+ (void)stream;
+ (void)count;
+}
+
+//! Write character to a stream, presuming buffer is reserved.
+template<typename Stream>
+inline void PutUnsafe(Stream& stream, typename Stream::Ch c) {
+ stream.Put(c);
+}
+
+//! Put N copies of a character to a stream.
+template<typename Stream, typename Ch>
+inline void PutN(Stream& stream, Ch c, size_t n) {
+ PutReserve(stream, n);
+ for (size_t i = 0; i < n; i++)
+ PutUnsafe(stream, c);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// StringStream
+
+//! Read-only string stream.
+/*! \note implements Stream concept
+*/
+template <typename Encoding>
+struct GenericStringStream {
+ typedef typename Encoding::Ch Ch;
+
+ GenericStringStream(const Ch *src) : src_(src), head_(src) {}
+
+ Ch Peek() const { return *src_; }
+ Ch Take() { return *src_++; }
+ size_t Tell() const { return static_cast<size_t>(src_ - head_); }
+
+ Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ void Put(Ch) { RAPIDJSON_ASSERT(false); }
+ void Flush() { RAPIDJSON_ASSERT(false); }
+ size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+ const Ch* src_; //!< Current read position.
+ const Ch* head_; //!< Original head of the string.
+};
+
+template <typename Encoding>
+struct StreamTraits<GenericStringStream<Encoding> > {
+ enum { copyOptimization = 1 };
+};
+
+//! String stream with UTF8 encoding.
+typedef GenericStringStream<UTF8<> > StringStream;
+
+///////////////////////////////////////////////////////////////////////////////
+// InsituStringStream
+
+//! A read-write string stream.
+/*! This string stream is particularly designed for in-situ parsing.
+ \note implements Stream concept
+*/
+template <typename Encoding>
+struct GenericInsituStringStream {
+ typedef typename Encoding::Ch Ch;
+
+ GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {}
+
+ // Read
+ Ch Peek() { return *src_; }
+ Ch Take() { return *src_++; }
+ size_t Tell() { return static_cast<size_t>(src_ - head_); }
+
+ // Write
+ void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; }
+
+ Ch* PutBegin() { return dst_ = src_; }
+ size_t PutEnd(Ch* begin) { return static_cast<size_t>(dst_ - begin); }
+ void Flush() {}
+
+ Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; }
+ void Pop(size_t count) { dst_ -= count; }
+
+ Ch* src_;
+ Ch* dst_;
+ Ch* head_;
+};
+
+template <typename Encoding>
+struct StreamTraits<GenericInsituStringStream<Encoding> > {
+ enum { copyOptimization = 1 };
+};
+
+//! Insitu string stream with UTF8 encoding.
+typedef GenericInsituStringStream<UTF8<> > InsituStringStream;
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_STREAM_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/stringbuffer.h b/ext/librethinkdbxx/src/rapidjson/stringbuffer.h
new file mode 100644
index 00000000..78f34d20
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/stringbuffer.h
@@ -0,0 +1,117 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_STRINGBUFFER_H_
+#define RAPIDJSON_STRINGBUFFER_H_
+
+#include "stream.h"
+#include "internal/stack.h"
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+#include <utility> // std::move
+#endif
+
+#include "internal/stack.h"
+
+#if defined(__clang__)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Represents an in-memory output stream.
+/*!
+ \tparam Encoding Encoding of the stream.
+ \tparam Allocator type for allocating memory buffer.
+ \note implements Stream concept
+*/
+template <typename Encoding, typename Allocator = CrtAllocator>
+class GenericStringBuffer {
+public:
+ typedef typename Encoding::Ch Ch;
+
+ GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {}
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {}
+ GenericStringBuffer& operator=(GenericStringBuffer&& rhs) {
+ if (&rhs != this)
+ stack_ = std::move(rhs.stack_);
+ return *this;
+ }
+#endif
+
+ void Put(Ch c) { *stack_.template Push<Ch>() = c; }
+ void PutUnsafe(Ch c) { *stack_.template PushUnsafe<Ch>() = c; }
+ void Flush() {}
+
+ void Clear() { stack_.Clear(); }
+ void ShrinkToFit() {
+ // Push and pop a null terminator. This is safe.
+ *stack_.template Push<Ch>() = '\0';
+ stack_.ShrinkToFit();
+ stack_.template Pop<Ch>(1);
+ }
+
+ void Reserve(size_t count) { stack_.template Reserve<Ch>(count); }
+ Ch* Push(size_t count) { return stack_.template Push<Ch>(count); }
+ Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe<Ch>(count); }
+ void Pop(size_t count) { stack_.template Pop<Ch>(count); }
+
+ const Ch* GetString() const {
+ // Push and pop a null terminator. This is safe.
+ *stack_.template Push<Ch>() = '\0';
+ stack_.template Pop<Ch>(1);
+
+ return stack_.template Bottom<Ch>();
+ }
+
+ size_t GetSize() const { return stack_.GetSize(); }
+
+ static const size_t kDefaultCapacity = 256;
+ mutable internal::Stack<Allocator> stack_;
+
+private:
+ // Prohibit copy constructor & assignment operator.
+ GenericStringBuffer(const GenericStringBuffer&);
+ GenericStringBuffer& operator=(const GenericStringBuffer&);
+};
+
+//! String buffer with UTF8 encoding
+typedef GenericStringBuffer<UTF8<> > StringBuffer;
+
+template<typename Encoding, typename Allocator>
+inline void PutReserve(GenericStringBuffer<Encoding, Allocator>& stream, size_t count) {
+ stream.Reserve(count);
+}
+
+template<typename Encoding, typename Allocator>
+inline void PutUnsafe(GenericStringBuffer<Encoding, Allocator>& stream, typename Encoding::Ch c) {
+ stream.PutUnsafe(c);
+}
+
+//! Implement specialized version of PutN() with memset() for better performance.
+template<>
+inline void PutN(GenericStringBuffer<UTF8<> >& stream, char c, size_t n) {
+ std::memset(stream.stack_.Push<char>(n), c, n * sizeof(c));
+}
+
+RAPIDJSON_NAMESPACE_END
+
+#if defined(__clang__)
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_STRINGBUFFER_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/writer.h b/ext/librethinkdbxx/src/rapidjson/writer.h
new file mode 100644
index 00000000..7d0610eb
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/writer.h
@@ -0,0 +1,609 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_WRITER_H_
+#define RAPIDJSON_WRITER_H_
+
+#include "stream.h"
+#include "internal/stack.h"
+#include "internal/strfunc.h"
+#include "internal/dtoa.h"
+#include "internal/itoa.h"
+#include "stringbuffer.h"
+#include <new> // placement new
+
+#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER)
+#include <intrin.h>
+#pragma intrinsic(_BitScanForward)
+#endif
+#ifdef RAPIDJSON_SSE42
+#include <nmmintrin.h>
+#elif defined(RAPIDJSON_SSE2)
+#include <emmintrin.h>
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+RAPIDJSON_DIAG_OFF(unreachable-code)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// WriteFlag
+
+/*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS
+ \ingroup RAPIDJSON_CONFIG
+ \brief User-defined kWriteDefaultFlags definition.
+
+ User can define this as any \c WriteFlag combinations.
+*/
+#ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS
+#define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags
+#endif
+
+//! Combination of writeFlags
+enum WriteFlag {
+ kWriteNoFlags = 0, //!< No flags are set.
+ kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings.
+ kWriteNanAndInfFlag = 2, //!< Allow writing of Inf, -Inf and NaN.
+ kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS
+};
+
+//! JSON writer
+/*! Writer implements the concept Handler.
+ It generates JSON text by events to an output os.
+
+ User may programmatically calls the functions of a writer to generate JSON text.
+
+ On the other side, a writer can also be passed to objects that generates events,
+
+ for example Reader::Parse() and Document::Accept().
+
+ \tparam OutputStream Type of output stream.
+ \tparam SourceEncoding Encoding of source string.
+ \tparam TargetEncoding Encoding of output stream.
+ \tparam StackAllocator Type of allocator for allocating memory of stack.
+ \note implements Handler concept
+*/
+template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags>
+class Writer {
+public:
+ typedef typename SourceEncoding::Ch Ch;
+
+ static const int kDefaultMaxDecimalPlaces = 324;
+
+ //! Constructor
+ /*! \param os Output stream.
+ \param stackAllocator User supplied allocator. If it is null, it will create a private one.
+ \param levelDepth Initial capacity of stack.
+ */
+ explicit
+ Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) :
+ os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {}
+
+ explicit
+ Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) :
+ os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {}
+
+ //! Reset the writer with a new stream.
+ /*!
+ This function reset the writer with a new stream and default settings,
+ in order to make a Writer object reusable for output multiple JSONs.
+
+ \param os New output stream.
+ \code
+ Writer<OutputStream> writer(os1);
+ writer.StartObject();
+ // ...
+ writer.EndObject();
+
+ writer.Reset(os2);
+ writer.StartObject();
+ // ...
+ writer.EndObject();
+ \endcode
+ */
+ void Reset(OutputStream& os) {
+ os_ = &os;
+ hasRoot_ = false;
+ level_stack_.Clear();
+ }
+
+ //! Checks whether the output is a complete JSON.
+ /*!
+ A complete JSON has a complete root object or array.
+ */
+ bool IsComplete() const {
+ return hasRoot_ && level_stack_.Empty();
+ }
+
+ int GetMaxDecimalPlaces() const {
+ return maxDecimalPlaces_;
+ }
+
+ //! Sets the maximum number of decimal places for double output.
+ /*!
+ This setting truncates the output with specified number of decimal places.
+
+ For example,
+
+ \code
+ writer.SetMaxDecimalPlaces(3);
+ writer.StartArray();
+ writer.Double(0.12345); // "0.123"
+ writer.Double(0.0001); // "0.0"
+ writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent)
+ writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent)
+ writer.EndArray();
+ \endcode
+
+ The default setting does not truncate any decimal places. You can restore to this setting by calling
+ \code
+ writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces);
+ \endcode
+ */
+ void SetMaxDecimalPlaces(int maxDecimalPlaces) {
+ maxDecimalPlaces_ = maxDecimalPlaces;
+ }
+
+ /*!@name Implementation of Handler
+ \see Handler
+ */
+ //@{
+
+ bool Null() { Prefix(kNullType); return WriteNull(); }
+ bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return WriteBool(b); }
+ bool Int(int i) { Prefix(kNumberType); return WriteInt(i); }
+ bool Uint(unsigned u) { Prefix(kNumberType); return WriteUint(u); }
+ bool Int64(int64_t i64) { Prefix(kNumberType); return WriteInt64(i64); }
+ bool Uint64(uint64_t u64) { Prefix(kNumberType); return WriteUint64(u64); }
+
+ //! Writes the given \c double value to the stream
+ /*!
+ \param d The value to be written.
+ \return Whether it is succeed.
+ */
+ bool Double(double d) { Prefix(kNumberType); return WriteDouble(d); }
+
+ bool RawNumber(const Ch* str, SizeType length, bool copy = false) {
+ (void)copy;
+ Prefix(kNumberType);
+ return WriteString(str, length);
+ }
+
+ bool String(const Ch* str, SizeType length, bool copy = false) {
+ (void)copy;
+ Prefix(kStringType);
+ return WriteString(str, length);
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ bool String(const std::basic_string<Ch>& str) {
+ return String(str.data(), SizeType(str.size()));
+ }
+#endif
+
+ bool StartObject() {
+ Prefix(kObjectType);
+ new (level_stack_.template Push<Level>()) Level(false);
+ return WriteStartObject();
+ }
+
+ bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); }
+
+ bool EndObject(SizeType memberCount = 0) {
+ (void)memberCount;
+ RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
+ RAPIDJSON_ASSERT(!level_stack_.template Top<Level>()->inArray);
+ level_stack_.template Pop<Level>(1);
+ bool ret = WriteEndObject();
+ if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text
+ os_->Flush();
+ return ret;
+ }
+
+ bool StartArray() {
+ Prefix(kArrayType);
+ new (level_stack_.template Push<Level>()) Level(true);
+ return WriteStartArray();
+ }
+
+ bool EndArray(SizeType elementCount = 0) {
+ (void)elementCount;
+ RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
+ RAPIDJSON_ASSERT(level_stack_.template Top<Level>()->inArray);
+ level_stack_.template Pop<Level>(1);
+ bool ret = WriteEndArray();
+ if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text
+ os_->Flush();
+ return ret;
+ }
+ //@}
+
+ /*! @name Convenience extensions */
+ //@{
+
+ //! Simpler but slower overload.
+ bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
+ bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); }
+
+ //@}
+
+ //! Write a raw JSON value.
+ /*!
+ For user to write a stringified JSON as a value.
+
+ \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range.
+ \param length Length of the json.
+ \param type Type of the root of json.
+ */
+ bool RawValue(const Ch* json, size_t length, Type type) { Prefix(type); return WriteRawValue(json, length); }
+
+protected:
+ //! Information for each nested level
+ struct Level {
+ Level(bool inArray_) : valueCount(0), inArray(inArray_) {}
+ size_t valueCount; //!< number of values in this level
+ bool inArray; //!< true if in array, otherwise in object
+ };
+
+ static const size_t kDefaultLevelDepth = 32;
+
+ bool WriteNull() {
+ PutReserve(*os_, 4);
+ PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true;
+ }
+
+ bool WriteBool(bool b) {
+ if (b) {
+ PutReserve(*os_, 4);
+ PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e');
+ }
+ else {
+ PutReserve(*os_, 5);
+ PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e');
+ }
+ return true;
+ }
+
+ bool WriteInt(int i) {
+ char buffer[11];
+ const char* end = internal::i32toa(i, buffer);
+ PutReserve(*os_, static_cast<size_t>(end - buffer));
+ for (const char* p = buffer; p != end; ++p)
+ PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(*p));
+ return true;
+ }
+
+ bool WriteUint(unsigned u) {
+ char buffer[10];
+ const char* end = internal::u32toa(u, buffer);
+ PutReserve(*os_, static_cast<size_t>(end - buffer));
+ for (const char* p = buffer; p != end; ++p)
+ PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(*p));
+ return true;
+ }
+
+ bool WriteInt64(int64_t i64) {
+ char buffer[21];
+ const char* end = internal::i64toa(i64, buffer);
+ PutReserve(*os_, static_cast<size_t>(end - buffer));
+ for (const char* p = buffer; p != end; ++p)
+ PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(*p));
+ return true;
+ }
+
+ bool WriteUint64(uint64_t u64) {
+ char buffer[20];
+ char* end = internal::u64toa(u64, buffer);
+ PutReserve(*os_, static_cast<size_t>(end - buffer));
+ for (char* p = buffer; p != end; ++p)
+ PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(*p));
+ return true;
+ }
+
+ bool WriteDouble(double d) {
+ if (internal::Double(d).IsNanOrInf()) {
+ if (!(writeFlags & kWriteNanAndInfFlag))
+ return false;
+ if (internal::Double(d).IsNan()) {
+ PutReserve(*os_, 3);
+ PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N');
+ return true;
+ }
+ if (internal::Double(d).Sign()) {
+ PutReserve(*os_, 9);
+ PutUnsafe(*os_, '-');
+ }
+ else
+ PutReserve(*os_, 8);
+ PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f');
+ PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y');
+ return true;
+ }
+
+ char buffer[25];
+ char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);
+ PutReserve(*os_, static_cast<size_t>(end - buffer));
+ for (char* p = buffer; p != end; ++p)
+ PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(*p));
+ return true;
+ }
+
+ bool WriteString(const Ch* str, SizeType length) {
+ static const typename TargetEncoding::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+ static const char escape[256] = {
+#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ //0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00
+ 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10
+ 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20
+ Z16, Z16, // 30~4F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50
+ Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF
+#undef Z16
+ };
+
+ if (TargetEncoding::supportUnicode)
+ PutReserve(*os_, 2 + length * 6); // "\uxxxx..."
+ else
+ PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..."
+
+ PutUnsafe(*os_, '\"');
+ GenericStringStream<SourceEncoding> is(str);
+ while (ScanWriteUnescapedString(is, length)) {
+ const Ch c = is.Peek();
+ if (!TargetEncoding::supportUnicode && static_cast<unsigned>(c) >= 0x80) {
+ // Unicode escaping
+ unsigned codepoint;
+ if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint)))
+ return false;
+ PutUnsafe(*os_, '\\');
+ PutUnsafe(*os_, 'u');
+ if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) {
+ PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]);
+ PutUnsafe(*os_, hexDigits[(codepoint >> 8) & 15]);
+ PutUnsafe(*os_, hexDigits[(codepoint >> 4) & 15]);
+ PutUnsafe(*os_, hexDigits[(codepoint ) & 15]);
+ }
+ else {
+ RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF);
+ // Surrogate pair
+ unsigned s = codepoint - 0x010000;
+ unsigned lead = (s >> 10) + 0xD800;
+ unsigned trail = (s & 0x3FF) + 0xDC00;
+ PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]);
+ PutUnsafe(*os_, hexDigits[(lead >> 8) & 15]);
+ PutUnsafe(*os_, hexDigits[(lead >> 4) & 15]);
+ PutUnsafe(*os_, hexDigits[(lead ) & 15]);
+ PutUnsafe(*os_, '\\');
+ PutUnsafe(*os_, 'u');
+ PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]);
+ PutUnsafe(*os_, hexDigits[(trail >> 8) & 15]);
+ PutUnsafe(*os_, hexDigits[(trail >> 4) & 15]);
+ PutUnsafe(*os_, hexDigits[(trail ) & 15]);
+ }
+ }
+ else if ((sizeof(Ch) == 1 || static_cast<unsigned>(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast<unsigned char>(c)])) {
+ is.Take();
+ PutUnsafe(*os_, '\\');
+ PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(escape[static_cast<unsigned char>(c)]));
+ if (escape[static_cast<unsigned char>(c)] == 'u') {
+ PutUnsafe(*os_, '0');
+ PutUnsafe(*os_, '0');
+ PutUnsafe(*os_, hexDigits[static_cast<unsigned char>(c) >> 4]);
+ PutUnsafe(*os_, hexDigits[static_cast<unsigned char>(c) & 0xF]);
+ }
+ }
+ else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ?
+ Transcoder<SourceEncoding, TargetEncoding>::Validate(is, *os_) :
+ Transcoder<SourceEncoding, TargetEncoding>::TranscodeUnsafe(is, *os_))))
+ return false;
+ }
+ PutUnsafe(*os_, '\"');
+ return true;
+ }
+
+ bool ScanWriteUnescapedString(GenericStringStream<SourceEncoding>& is, size_t length) {
+ return RAPIDJSON_LIKELY(is.Tell() < length);
+ }
+
+ bool WriteStartObject() { os_->Put('{'); return true; }
+ bool WriteEndObject() { os_->Put('}'); return true; }
+ bool WriteStartArray() { os_->Put('['); return true; }
+ bool WriteEndArray() { os_->Put(']'); return true; }
+
+ bool WriteRawValue(const Ch* json, size_t length) {
+ PutReserve(*os_, length);
+ for (size_t i = 0; i < length; i++) {
+ RAPIDJSON_ASSERT(json[i] != '\0');
+ PutUnsafe(*os_, json[i]);
+ }
+ return true;
+ }
+
+ void Prefix(Type type) {
+ (void)type;
+ if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root
+ Level* level = level_stack_.template Top<Level>();
+ if (level->valueCount > 0) {
+ if (level->inArray)
+ os_->Put(','); // add comma if it is not the first element in array
+ else // in object
+ os_->Put((level->valueCount % 2 == 0) ? ',' : ':');
+ }
+ if (!level->inArray && level->valueCount % 2 == 0)
+ RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name
+ level->valueCount++;
+ }
+ else {
+ RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root.
+ hasRoot_ = true;
+ }
+ }
+
+ OutputStream* os_;
+ internal::Stack<StackAllocator> level_stack_;
+ int maxDecimalPlaces_;
+ bool hasRoot_;
+
+private:
+ // Prohibit copy constructor & assignment operator.
+ Writer(const Writer&);
+ Writer& operator=(const Writer&);
+};
+
+// Full specialization for StringStream to prevent memory copying
+
+template<>
+inline bool Writer<StringBuffer>::WriteInt(int i) {
+ char *buffer = os_->Push(11);
+ const char* end = internal::i32toa(i, buffer);
+ os_->Pop(static_cast<size_t>(11 - (end - buffer)));
+ return true;
+}
+
+template<>
+inline bool Writer<StringBuffer>::WriteUint(unsigned u) {
+ char *buffer = os_->Push(10);
+ const char* end = internal::u32toa(u, buffer);
+ os_->Pop(static_cast<size_t>(10 - (end - buffer)));
+ return true;
+}
+
+template<>
+inline bool Writer<StringBuffer>::WriteInt64(int64_t i64) {
+ char *buffer = os_->Push(21);
+ const char* end = internal::i64toa(i64, buffer);
+ os_->Pop(static_cast<size_t>(21 - (end - buffer)));
+ return true;
+}
+
+template<>
+inline bool Writer<StringBuffer>::WriteUint64(uint64_t u) {
+ char *buffer = os_->Push(20);
+ const char* end = internal::u64toa(u, buffer);
+ os_->Pop(static_cast<size_t>(20 - (end - buffer)));
+ return true;
+}
+
+template<>
+inline bool Writer<StringBuffer>::WriteDouble(double d) {
+ if (internal::Double(d).IsNanOrInf()) {
+ // Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag).
+ if (!(kWriteDefaultFlags & kWriteNanAndInfFlag))
+ return false;
+ if (internal::Double(d).IsNan()) {
+ PutReserve(*os_, 3);
+ PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N');
+ return true;
+ }
+ if (internal::Double(d).Sign()) {
+ PutReserve(*os_, 9);
+ PutUnsafe(*os_, '-');
+ }
+ else
+ PutReserve(*os_, 8);
+ PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f');
+ PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y');
+ return true;
+ }
+
+ char *buffer = os_->Push(25);
+ char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);
+ os_->Pop(static_cast<size_t>(25 - (end - buffer)));
+ return true;
+}
+
+#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42)
+template<>
+inline bool Writer<StringBuffer>::ScanWriteUnescapedString(StringStream& is, size_t length) {
+ if (length < 16)
+ return RAPIDJSON_LIKELY(is.Tell() < length);
+
+ if (!RAPIDJSON_LIKELY(is.Tell() < length))
+ return false;
+
+ const char* p = is.src_;
+ const char* end = is.head_ + length;
+ const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+ const char* endAligned = reinterpret_cast<const char*>(reinterpret_cast<size_t>(end) & static_cast<size_t>(~15));
+ if (nextAligned > end)
+ return true;
+
+ while (p != nextAligned)
+ if (*p < 0x20 || *p == '\"' || *p == '\\') {
+ is.src_ = p;
+ return RAPIDJSON_LIKELY(is.Tell() < length);
+ }
+ else
+ os_->PutUnsafe(*p++);
+
+ // The rest of string using SIMD
+ static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' };
+ static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' };
+ static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 };
+ const __m128i dq = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&dquote[0]));
+ const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
+ const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
+
+ for (; p != endAligned; p += 16) {
+ const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
+ const __m128i t1 = _mm_cmpeq_epi8(s, dq);
+ const __m128i t2 = _mm_cmpeq_epi8(s, bs);
+ const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19
+ const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3);
+ unsigned short r = static_cast<unsigned short>(_mm_movemask_epi8(x));
+ if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped
+ SizeType len;
+#ifdef _MSC_VER // Find the index of first escaped
+ unsigned long offset;
+ _BitScanForward(&offset, r);
+ len = offset;
+#else
+ len = static_cast<SizeType>(__builtin_ffs(r) - 1);
+#endif
+ char* q = reinterpret_cast<char*>(os_->PushUnsafe(len));
+ for (size_t i = 0; i < len; i++)
+ q[i] = p[i];
+
+ p += len;
+ break;
+ }
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s);
+ }
+
+ is.src_ = p;
+ return RAPIDJSON_LIKELY(is.Tell() < length);
+}
+#endif // defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42)
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_POP
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_RAPIDJSON_H_
diff --git a/ext/librethinkdbxx/src/term.cc b/ext/librethinkdbxx/src/term.cc
new file mode 100644
index 00000000..711ef27d
--- /dev/null
+++ b/ext/librethinkdbxx/src/term.cc
@@ -0,0 +1,285 @@
+#include <cstdlib>
+#include <set>
+
+#include "term.h"
+#include "json_p.h"
+
+namespace RethinkDB {
+
+using TT = Protocol::Term::TermType;
+
+struct {
+ Datum operator() (const Array& array) {
+ Array copy;
+ copy.reserve(array.size());
+ for (const auto& it : array) {
+ copy.emplace_back(it.apply<Datum>(*this));
+ }
+ return Datum(Array{TT::MAKE_ARRAY, std::move(copy)});
+ }
+ Datum operator() (const Object& object) {
+ Object copy;
+ for (const auto& it : object) {
+ copy.emplace(it.first, it.second.apply<Datum>(*this));
+ }
+ return std::move(copy);
+ }
+ template<class T>
+ Datum operator() (T&& atomic) {
+ return Datum(std::forward<T>(atomic));
+ }
+} datum_to_term;
+
+Term::Term(Datum&& datum_) : datum(datum_.apply<Datum>(datum_to_term)) { }
+Term::Term(const Datum& datum_) : datum(datum_.apply<Datum>(datum_to_term)) { }
+
+Term::Term(Term&& orig, OptArgs&& new_optargs) : datum(Nil()) {
+ Datum* cur = orig.datum.get_nth(2);
+ Object optargs;
+ free_vars = std::move(orig.free_vars);
+ if (cur) {
+ optargs = std::move(cur->extract_object());
+ }
+ for (auto& it : new_optargs) {
+ optargs.emplace(std::move(it.first), alpha_rename(std::move(it.second)));
+ }
+ datum = Array{ std::move(orig.datum.extract_nth(0)), std::move(orig.datum.extract_nth(1)), std::move(optargs) };
+}
+
+Term nil() {
+ return Term(Nil());
+}
+
+Cursor Term::run(Connection& conn, OptArgs&& opts) {
+ if (!free_vars.empty()) {
+ throw Error("run: term has free variables");
+ }
+
+ return conn.start_query(this, std::move(opts));
+}
+
+struct {
+ Datum operator() (Object&& object, const std::map<int, int>& subst, bool) {
+ Object ret;
+ for (auto& it : object) {
+ ret.emplace(std::move(it.first), std::move(it.second).apply<Datum>(*this, subst, false));
+ }
+ return ret;
+ }
+ Datum operator() (Array&& array, const std::map<int, int>& subst, bool args) {
+ if (!args) {
+ double cmd = array[0].extract_number();
+ if (cmd == static_cast<int>(TT::VAR)) {
+ double var = array[1].extract_nth(0).extract_number();
+ auto it = subst.find(static_cast<int>(var));
+ if (it != subst.end()) {
+ return Array{ TT::VAR, { it->second }};
+ }
+ }
+ if (array.size() == 2) {
+ return Array{ std::move(array[0]), std::move(array[1]).apply<Datum>(*this, subst, true) };
+ } else {
+ return Array{
+ std::move(array[0]),
+ std::move(array[1]).apply<Datum>(*this, subst, true),
+ std::move(array[2]).apply<Datum>(*this, subst, false) };
+ }
+ } else {
+ Array ret;
+ for (auto& it : array) {
+ ret.emplace_back(std::move(it).apply<Datum>(*this, subst, false));
+ }
+ return ret;
+ }
+ }
+ template <class T>
+ Datum operator() (T&& a, const std::map<int, int>&, bool) {
+ return std::move(a);
+ }
+} alpha_renamer;
+
+static int new_var_id(const std::map<int, int*>& vars) {
+ while (true) {
+ int id = gen_var_id();
+ if (vars.find(id) == vars.end()) {
+ return id;
+ }
+ }
+}
+
+Datum Term::alpha_rename(Term&& term) {
+ if (free_vars.empty()) {
+ free_vars = std::move(term.free_vars);
+ return std::move(term.datum);
+ }
+
+ std::map<int, int> subst;
+ for (auto it = term.free_vars.begin(); it != term.free_vars.end(); ++it) {
+ auto var = free_vars.find(it->first);
+ if (var == free_vars.end()) {
+ free_vars.emplace(it->first, it->second);
+ } else if (var->second != it->second) {
+ int id = new_var_id(free_vars);
+ subst.emplace(it->first, id);
+ free_vars.emplace(id, it->second);
+ }
+ }
+ if (subst.empty()) {
+ return std::move(term.datum);
+ } else {
+ return term.datum.apply<Datum>(alpha_renamer, subst, false);
+ }
+}
+
+int gen_var_id() {
+ return ::random() % (1<<30);
+}
+
+C0_IMPL(db_list, DB_LIST)
+C0_IMPL(table_list, TABLE_LIST)
+C0_IMPL(random, RANDOM)
+C0_IMPL(now, NOW)
+C0_IMPL(range, RANGE)
+C0_IMPL(error, ERROR)
+C0_IMPL(uuid, UUID)
+C0_IMPL(literal, LITERAL)
+CO0_IMPL(wait, WAIT)
+C0_IMPL(rebalance, REBALANCE)
+CO0_IMPL(random, RANDOM)
+
+Term row(TT::IMPLICIT_VAR, {});
+Term minval(TT::MINVAL, {});
+Term maxval(TT::MAXVAL, {});
+
+Term binary(const std::string& data) {
+ return expr(Binary(data));
+}
+
+Term binary(std::string&& data) {
+ return expr(Binary(data));
+}
+
+Term binary(const char* data) {
+ return expr(Binary(data));
+}
+
+struct {
+ bool operator() (const Object& object) {
+ for (const auto& it : object) {
+ if (it.second.apply<bool>(*this)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ bool operator() (const Array& array) {
+ int type = *array[0].get_number();
+ if (type == static_cast<int>(TT::IMPLICIT_VAR)) {
+ return true;
+ }
+ if (type == static_cast<int>(TT::FUNC)) {
+ return false;
+ }
+ for (const auto& it : *array[1].get_array()) {
+ if (it.apply<bool>(*this)) {
+ return true;
+ }
+ }
+ if (array.size() == 3) {
+ return array[2].apply<bool>(*this);
+ } else {
+ return false;
+ }
+ }
+ template <class T>
+ bool operator() (T) {
+ return false;
+ }
+} needs_func_wrap;
+
+Term Term::func_wrap(Term&& term) {
+ if (term.datum.apply<bool>(needs_func_wrap)) {
+ return Term(TT::FUNC, {expr(Array{new_var_id(term.free_vars)}), std::move(term)});
+ }
+ return term;
+}
+
+Term Term::func_wrap(const Term& term) {
+ if (term.datum.apply<bool>(needs_func_wrap)) {
+ // TODO return Term(TT::FUNC, {expr(Array{new_var_id(Term.free_vars)}), Term.copy()});
+ return Term(Nil());
+ }
+ return term;
+}
+
+Term Term::make_object(std::vector<Term>&& args) {
+ if (args.size() % 2 != 0) {
+ return Term(TT::OBJECT, std::move(args));
+ }
+ std::set<std::string> keys;
+ for (auto it = args.begin(); it != args.end() && it + 1 != args.end(); it += 2) {
+ std::string* key = it->datum.get_string();
+ if (!key || keys.count(*key)) {
+ return Term(TT::OBJECT, std::move(args));
+ }
+ keys.insert(*key);
+ }
+ Term ret{Nil()};
+ Object object;
+ for (auto it = args.begin(); it != args.end(); it += 2) {
+ std::string* key = it->datum.get_string();
+ object.emplace(std::move(*key), ret.alpha_rename(std::move(*(it + 1))));
+ }
+ ret.datum = std::move(object);
+ return ret;
+}
+
+Term Term::make_binary(Term&& term) {
+ std::string* string = term.datum.get_string();
+ if (string) {
+ return expr(Binary(std::move(*string)));
+ }
+ return Term(TT::BINARY, std::vector<Term>{term});
+}
+
+Term::Term(OptArgs&& optargs) : datum(Nil()) {
+ Object oargs;
+ for (auto& it : optargs) {
+ oargs.emplace(it.first, alpha_rename(std::move(it.second)));
+ }
+ datum = std::move(oargs);
+}
+
+OptArgs optargs() {
+ return OptArgs{};
+}
+
+Term january(TT::JANUARY, {});
+Term february(TT::FEBRUARY, {});
+Term march(TT::MARCH, {});
+Term april(TT::APRIL, {});
+Term may(TT::MAY, {});
+Term june(TT::JUNE, {});
+Term july(TT::JULY, {});
+Term august(TT::AUGUST, {});
+Term september(TT::SEPTEMBER, {});
+Term october(TT::OCTOBER, {});
+Term november(TT::NOVEMBER, {});
+Term december(TT::DECEMBER, {});
+Term monday(TT::MONDAY, {});
+Term tuesday(TT::TUESDAY, {});
+Term wednesday(TT::WEDNESDAY, {});
+Term thursday(TT::THURSDAY, {});
+Term friday(TT::FRIDAY, {});
+Term saturday(TT::SATURDAY, {});
+Term sunday(TT::SUNDAY, {});
+
+Term Term::copy() const {
+ return *this;
+}
+
+Datum Term::get_datum() const {
+ return datum;
+}
+
+}
diff --git a/ext/librethinkdbxx/src/term.h b/ext/librethinkdbxx/src/term.h
new file mode 100644
index 00000000..cdee5ec4
--- /dev/null
+++ b/ext/librethinkdbxx/src/term.h
@@ -0,0 +1,592 @@
+#pragma once
+
+#include "datum.h"
+#include "connection.h"
+#include "protocol_defs.h"
+#include "cursor.h"
+
+namespace RethinkDB {
+
+using TT = Protocol::Term::TermType;
+
+class Term;
+class Var;
+
+// An alias for the Term constructor
+template <class T>
+Term expr(T&&);
+
+int gen_var_id();
+
+// Can be used as the last argument to some ReQL commands that expect named arguments
+using OptArgs = std::map<std::string, Term>;
+
+// Represents a ReQL Term (RethinkDB Query Language)
+// Designed to be used with r-value *this
+class Term {
+public:
+ Term(const Term& other) = default;
+ Term(Term&& other) = default;
+ Term& operator= (const Term& other) = default;
+ Term& operator= (Term&& other) = default;
+
+ explicit Term(Datum&&);
+ explicit Term(const Datum&);
+ explicit Term(OptArgs&&);
+
+ // Create a copy of the Term
+ Term copy() const;
+
+ Term(std::function<Term()> f) : datum(Nil()) { set_function<std::function<Term()>>(f); }
+ Term(std::function<Term(Var)> f) : datum(Nil()) { set_function<std::function<Term(Var)>, 0>(f); }
+ Term(std::function<Term(Var, Var)> f) : datum(Nil()) { set_function<std::function<Term(Var, Var)>, 0, 1>(f); }
+ Term(std::function<Term(Var, Var, Var)> f) : datum(Nil()) { set_function<std::function<Term(Var, Var, Var)>, 0, 1, 2>(f); }
+ Term(Protocol::Term::TermType type, std::vector<Term>&& args) : datum(Array()) {
+ Array dargs;
+ for (auto& it : args) {
+ dargs.emplace_back(alpha_rename(std::move(it)));
+ }
+ datum = Datum(Array{ type, std::move(dargs) });
+ }
+
+ Term(Protocol::Term::TermType type, std::vector<Term>&& args, OptArgs&& optargs) : datum(Array()) {
+ Array dargs;
+ for (auto& it : args) {
+ dargs.emplace_back(alpha_rename(std::move(it)));
+ }
+ Object oargs;
+ for (auto& it : optargs) {
+ oargs.emplace(it.first, alpha_rename(std::move(it.second)));
+ }
+ datum = Array{ type, std::move(dargs), std::move(oargs) };
+ }
+
+ // Used internally to support row
+ static Term func_wrap(Term&&);
+ static Term func_wrap(const Term&);
+
+
+ // These macros are used to define most ReQL commands
+ // * Cn represents a method with n arguments
+ // * COn represents a method with n arguments and optional named arguments
+ // * C_ represents a method with any number of arguments
+ // Each method is implemented twice, once with r-value *this, and once with const *this
+ // The third argument, wrap, allows converting arguments into functions if they contain row
+
+#define C0(name, type) \
+ Term name() && { return Term(TT::type, std::vector<Term>{ std::move(*this) }); } \
+ Term name() const & { return Term(TT::type, std::vector<Term>{ *this }); }
+#define C1(name, type, wrap) \
+ template <class T> \
+ Term name(T&& a) && { return Term(TT::type, std::vector<Term>{ std::move(*this), wrap(expr(std::forward<T>(a))) }); } \
+ template <class T> \
+ Term name(T&& a) const & { return Term(TT::type, std::vector<Term>{ *this, wrap(expr(std::forward<T>(a))) }); }
+#define C2(name, type) \
+ template <class T, class U> Term name(T&& a, U&& b) && { \
+ return Term(TT::type, std::vector<Term>{ std::move(*this), \
+ expr(std::forward<T>(a)), expr(std::forward<U>(b)) }); } \
+ template <class T, class U> Term name(T&& a, U&& b) const & { \
+ return Term(TT::type, std::vector<Term>{ *this, \
+ expr(std::forward<T>(a)), expr(std::forward<U>(b)) }); }
+#define C_(name, type, wrap) \
+ template <class ...T> Term name(T&& ...a) && { \
+ return Term(TT::type, std::vector<Term>{ std::move(*this), \
+ wrap(expr(std::forward<T>(a)))... }); } \
+ template <class ...T> Term name(T&& ...a) const & { \
+ return Term(TT::type, std::vector<Term>{ *this, \
+ wrap(expr(std::forward<T>(a)))... }); }
+#define CO0(name, type) \
+ Term name(OptArgs&& optarg = {}) && { \
+ return Term(TT::type, std::vector<Term>{ std::move(*this) }, std::move(optarg)); } \
+ Term name(OptArgs&& optarg = {}) const & { \
+ return Term(TT::type, std::vector<Term>{ *this }, std::move(optarg)); }
+#define CO1(name, type, wrap) \
+ template <class T> Term name(T&& a, OptArgs&& optarg = {}) && { \
+ return Term(TT::type, std::vector<Term>{ std::move(*this), \
+ wrap(expr(std::forward<T>(a))) }, std::move(optarg)); } \
+ template <class T> Term name(T&& a, OptArgs&& optarg = {}) const & { \
+ return Term(TT::type, std::vector<Term>{ *this, \
+ wrap(expr(std::forward<T>(a))) }, std::move(optarg)); }
+#define CO2(name, type, wrap) \
+ template <class T, class U> Term name(T&& a, U&& b, OptArgs&& optarg = {}) && { \
+ return Term(TT::type, std::vector<Term>{ std::move(*this), \
+ wrap(expr(std::forward<T>(a))), wrap(expr(std::forward<U>(b))) }, std::move(optarg)); } \
+ template <class T, class U> Term name(T&& a, U&& b, OptArgs&& optarg = {}) const & { \
+ return Term(TT::type, std::vector<Term>{ *this, \
+ wrap(expr(std::forward<T>(a))), wrap(expr(std::forward<U>(b))) }, std::move(optarg)); }
+#define CO3(name, type, wrap) \
+ template <class T, class U, class V> Term name(T&& a, U&& b, V&& c, OptArgs&& optarg = {}) && { \
+ return Term(TT::type, std::vector<Term>{ std::move(*this), \
+ wrap(expr(std::forward<T>(a))), wrap(expr(std::forward<U>(b))), \
+ wrap(expr(std::forward<V>(c))) }, std::move(optarg)); } \
+ template <class T, class U, class V> Term name(T&& a, U&& b, V&& c, OptArgs&& optarg = {}) const & { \
+ return Term(TT::type, std::vector<Term>{ *this, \
+ wrap(expr(std::forward<T>(a))), wrap(expr(std::forward<U>(b))), \
+ wrap(expr(std::forward<V>(c)))}, std::move(optarg)); }
+#define CO4(name, type, wrap) \
+ template <class T, class U, class V, class W> Term name(T&& a, U&& b, V&& c, W&& d, OptArgs&& optarg = {}) && { \
+ return Term(TT::type, std::vector<Term>{ std::move(*this), \
+ wrap(expr(std::forward<T>(a))), wrap(expr(std::forward<U>(b))), \
+ wrap(expr(std::forward<V>(c))), wrap(expr(std::forward<W>(d))) }, std::move(optarg)); } \
+ template <class T, class U, class V, class W> Term name(T&& a, U&& b, V&& c, W&& d, OptArgs&& optarg = {}) const & { \
+ return Term(TT::type, std::vector<Term>{ *this, \
+ wrap(expr(std::forward<T>(a))), wrap(expr(std::forward<U>(b))), \
+ wrap(expr(std::forward<V>(c))), wrap(expr(std::forward<W>(d))) }, std::move(optarg)); }
+#define CO_(name, type, wrap) \
+ C_(name, type, wrap) \
+ CO0(name, type) \
+ CO1(name, type, wrap) \
+ CO2(name, type, wrap) \
+ CO3(name, type, wrap) \
+ CO4(name, type, wrap)
+#define no_wrap(x) x
+
+ CO1(table_create, TABLE_CREATE, no_wrap)
+ C1(table_drop, TABLE_DROP, no_wrap)
+ C0(table_list, TABLE_LIST)
+ CO1(index_create, INDEX_CREATE, no_wrap)
+ CO2(index_create, INDEX_CREATE, func_wrap)
+ C1(index_drop, INDEX_DROP, no_wrap)
+ C0(index_list, INDEX_LIST)
+ CO2(index_rename, INDEX_RENAME, no_wrap)
+ C_(index_status, INDEX_STATUS, no_wrap)
+ C_(index_wait, INDEX_WAIT, no_wrap)
+ CO0(changes, CHANGES)
+ CO1(insert, INSERT, no_wrap)
+ CO1(update, UPDATE, func_wrap)
+ CO1(replace, REPLACE, func_wrap)
+ CO0(delete_, DELETE)
+ C0(sync, SYNC)
+ CO1(table, TABLE, no_wrap)
+ C1(get, GET, no_wrap)
+ CO_(get_all, GET_ALL, no_wrap)
+ CO2(between, BETWEEN, no_wrap)
+ CO1(filter, FILTER, func_wrap)
+ C2(inner_join, INNER_JOIN)
+ C2(outer_join, OUTER_JOIN)
+ CO2(eq_join, EQ_JOIN, func_wrap)
+ C0(zip, ZIP)
+ C_(map, MAP, func_wrap)
+ C_(with_fields, WITH_FIELDS, no_wrap)
+ C1(concat_map, CONCAT_MAP, func_wrap)
+ CO_(order_by, ORDER_BY, func_wrap)
+ C1(skip, SKIP, no_wrap)
+ C1(limit, LIMIT, no_wrap)
+ CO1(slice, SLICE, no_wrap)
+ CO2(slice, SLICE, no_wrap)
+ C1(nth, NTH, no_wrap)
+ C1(offsets_of, OFFSETS_OF, func_wrap)
+ C0(is_empty, IS_EMPTY)
+ CO_(union_, UNION, no_wrap)
+ C1(sample, SAMPLE, no_wrap)
+ CO_(group, GROUP, func_wrap)
+ C0(ungroup, UNGROUP)
+ C1(reduce, REDUCE, no_wrap)
+ CO2(fold, FOLD, no_wrap)
+ C0(count, COUNT)
+ C1(count, COUNT, func_wrap)
+ C0(sum, SUM)
+ C1(sum, SUM, func_wrap)
+ C0(avg, AVG)
+ C1(avg, AVG, func_wrap)
+ C1(min, MIN, func_wrap)
+ CO0(min, MIN)
+ C1(max, MAX, func_wrap)
+ CO0(max, MAX)
+ CO0(distinct, DISTINCT)
+ C_(contains, CONTAINS, func_wrap)
+ C_(pluck, PLUCK, no_wrap)
+ C_(without, WITHOUT, no_wrap)
+ C_(merge, MERGE, func_wrap)
+ C1(append, APPEND, no_wrap)
+ C1(prepend, PREPEND, no_wrap)
+ C1(difference, DIFFERENCE, no_wrap)
+ C1(set_insert, SET_INSERT, no_wrap)
+ C1(set_union, SET_UNION, no_wrap)
+ C1(set_intersection, SET_INTERSECTION, no_wrap)
+ C1(set_difference, SET_DIFFERENCE, no_wrap)
+ C1(operator[], BRACKET, no_wrap)
+ C1(get_field, GET_FIELD, no_wrap)
+ C_(has_fields, HAS_FIELDS, no_wrap)
+ C2(insert_at, INSERT_AT)
+ C2(splice_at, SPLICE_AT)
+ C1(delete_at, DELETE_AT, no_wrap)
+ C2(delete_at, DELETE_AT)
+ C2(change_at, CHANGE_AT)
+ C0(keys, KEYS)
+ C1(match, MATCH, no_wrap)
+ C0(split, SPLIT)
+ C1(split, SPLIT, no_wrap)
+ C2(split, SPLIT)
+ C0(upcase, UPCASE)
+ C0(downcase, DOWNCASE)
+ C_(add, ADD, no_wrap)
+ C1(operator+, ADD, no_wrap)
+ C_(sub, SUB, no_wrap)
+ C1(operator-, SUB, no_wrap)
+ C_(mul, MUL, no_wrap)
+ C1(operator*, MUL, no_wrap)
+ C_(div, DIV, no_wrap)
+ C1(operator/, DIV, no_wrap)
+ C1(mod, MOD, no_wrap)
+ C1(operator%, MOD, no_wrap)
+ C_(and_, AND, no_wrap)
+ C1(operator&&, AND, no_wrap)
+ C_(or_, OR, no_wrap)
+ C1(operator||, OR, no_wrap)
+ C1(eq, EQ, no_wrap)
+ C1(operator==, EQ, no_wrap)
+ C1(ne, NE, no_wrap)
+ C1(operator!=, NE, no_wrap)
+ C1(gt, GT, no_wrap)
+ C1(operator>, GT, no_wrap)
+ C1(ge, GE, no_wrap)
+ C1(operator>=, GE, no_wrap)
+ C1(lt, LT, no_wrap)
+ C1(operator<, LT, no_wrap)
+ C1(le, LE, no_wrap)
+ C1(operator<=, LE, no_wrap)
+ C0(not_, NOT)
+ C0(operator!, NOT)
+ C1(in_timezone, IN_TIMEZONE, no_wrap)
+ C0(timezone, TIMEZONE)
+ CO2(during, DURING, no_wrap)
+ C0(date, DATE)
+ C0(time_of_day, TIME_OF_DAY)
+ C0(year, YEAR)
+ C0(month, MONTH)
+ C0(day, DAY)
+ C0(day_of_week, DAY_OF_WEEK)
+ C0(day_of_year, DAY_OF_YEAR)
+ C0(hours, HOURS)
+ C0(minutes, MINUTES)
+ C0(seconds, SECONDS)
+ C0(to_iso8601, TO_ISO8601)
+ C0(to_epoch_time, TO_EPOCH_TIME)
+ C1(for_each, FOR_EACH, func_wrap)
+ C1(default_, DEFAULT, no_wrap)
+ CO1(js, JAVASCRIPT, no_wrap)
+ C1(coerce_to, COERCE_TO, no_wrap)
+ C0(type_of, TYPE_OF)
+ C0(info, INFO)
+ C0(to_json, TO_JSON_STRING)
+ C0(to_json_string, TO_JSON_STRING)
+ C1(distance, DISTANCE, no_wrap)
+ C0(fill, FILL)
+ C0(to_geojson, TO_GEOJSON)
+ CO1(get_intersecting, GET_INTERSECTING, no_wrap)
+ CO1(get_nearest, GET_NEAREST, no_wrap)
+ C1(includes, INCLUDES, no_wrap)
+ C1(intersects, INTERSECTS, no_wrap)
+ C1(polygon_sub, POLYGON_SUB, no_wrap)
+ C0(config, CONFIG)
+ C0(rebalance, REBALANCE)
+ CO0(reconfigure, RECONFIGURE)
+ C0(status, STATUS)
+ CO0(wait, WAIT)
+ C0(floor, FLOOR)
+ C0(ceil, CEIL)
+ C0(round, ROUND)
+ C0(values, VALUES)
+
+ // The expansion of this macro fails to compile on some versions of GCC and Clang:
+ // C_(operator(), FUNCALL, no_wrap)
+ // The std::enable_if makes the error go away
+
+ // $doc(do)
+
+ template <class T, class ...U>
+ typename std::enable_if<!std::is_same<T, Var>::value, Term>::type
+ operator() (T&& a, U&& ...b) && {
+ return Term(TT::FUNCALL, std::vector<Term>{
+ std::move(*this),
+ expr(std::forward<T>(a)),
+ expr(std::forward<U>(b))... });
+ }
+ template <class T, class ...U>
+ typename std::enable_if<!std::is_same<T, Var>::value, Term>::type
+ operator() (T&& a, U&& ...b) const & {
+ return Term(TT::FUNCALL, std::vector<Term>{
+ *this,
+ expr(std::forward<T>(a)),
+ expr(std::forward<U>(b))... });
+ }
+
+#undef C0
+#undef C1
+#undef C2
+#undef C_
+#undef CO0
+#undef CO1
+#undef CO2
+
+ // Send the term to the server and return the results.
+ // Errors returned by the server are thrown.
+ Cursor run(Connection&, OptArgs&& args = {});
+
+ // $doc(do)
+ template <class ...T>
+ Term do_(T&& ...a) && {
+ auto list = { std::move(*this), Term::func_wrap(expr(std::forward<T>(a)))... };
+ std::vector<Term> args;
+ args.reserve(list.size() + 1);
+ args.emplace_back(func_wrap(std::move(*(list.end()-1))));
+ for (auto it = list.begin(); it + 1 != list.end(); ++it) {
+ args.emplace_back(std::move(*it));
+ }
+ return Term(TT::FUNCALL, std::move(args));
+ }
+
+ // Adds optargs to an already built term
+ Term opt(OptArgs&& optargs) && {
+ return Term(std::move(*this), std::move(optargs));
+ }
+
+ // Used internally to implement object()
+ static Term make_object(std::vector<Term>&&);
+
+ // Used internally to implement array()
+ static Term make_binary(Term&&);
+
+ Datum get_datum() const;
+
+private:
+ friend class Var;
+ friend class Connection;
+ friend struct Query;
+
+ template <int _>
+ Var mkvar(std::vector<int>& vars);
+
+ template <class F, int ...N>
+ void set_function(F);
+
+ Datum alpha_rename(Term&&);
+
+ Term(Term&& orig, OptArgs&& optargs);
+
+ std::map<int, int*> free_vars;
+ Datum datum;
+};
+
+// A term representing null
+Term nil();
+
+template <class T>
+Term expr(T&& a) {
+ return Term(std::forward<T>(a));
+}
+
+// Represents a ReQL variable.
+// This type is passed to functions used in ReQL queries.
+class Var {
+public:
+ // Convert to a term
+ Term operator*() const {
+ Term term(TT::VAR, std::vector<Term>{expr(*id)});
+ term.free_vars = {{*id, id}};
+ return term;
+ }
+
+ Var(int* id_) : id(id_) { }
+private:
+ int* id;
+};
+
+template <int N>
+Var Term::mkvar(std::vector<int>& vars) {
+ int id = gen_var_id();
+ vars.push_back(id);
+ return Var(&*vars.rbegin());
+}
+
+template <class F, int ...N>
+void Term::set_function(F f) {
+ std::vector<int> vars;
+ vars.reserve(sizeof...(N));
+ std::vector<Var> args = { mkvar<N>(vars)... };
+ Term body = f(args[N] ...);
+
+ int* low = &*vars.begin();
+ int* high = &*(vars.end() - 1);
+ for (auto it = body.free_vars.begin(); it != body.free_vars.end(); ) {
+ if (it->second >= low && it->second <= high) {
+ if (it->first != *it->second) {
+ throw Error("Internal error: variable index mis-match");
+ }
+ ++it;
+ } else {
+ free_vars.emplace(*it);
+ ++it;
+ }
+ }
+ datum = Array{TT::FUNC, Array{Array{TT::MAKE_ARRAY, vars}, body.datum}};
+}
+
+// These macros are similar to those defined above, but for top-level ReQL operations
+
+#define C0(name) Term name();
+#define C0_IMPL(name, type) Term name() { return Term(TT::type, std::vector<Term>{}); }
+#define CO0(name) Term name(OptArgs&& optargs = {});
+#define CO0_IMPL(name, type) Term name(OptArgs&& optargs) { return Term(TT::type, std::vector<Term>{}, std::move(optargs)); }
+#define C1(name, type, wrap) template <class T> Term name(T&& a) { \
+ return Term(TT::type, std::vector<Term>{ wrap(expr(std::forward<T>(a))) }); }
+#define C2(name, type) template <class T, class U> Term name(T&& a, U&& b) { \
+ return Term(TT::type, std::vector<Term>{ expr(std::forward<T>(a)), expr(std::forward<U>(b)) }); }
+#define C3(name, type) template <class A, class B, class C> \
+ Term name(A&& a, B&& b, C&& c) { return Term(TT::type, std::vector<Term>{ \
+ expr(std::forward<A>(a)), expr(std::forward<B>(b)), expr(std::forward<C>(c)) }); }
+#define C4(name, type) template <class A, class B, class C, class D> \
+ Term name(A&& a, B&& b, C&& c, D&& d) { return Term(TT::type, std::vector<Term>{ \
+ expr(std::forward<A>(a)), expr(std::forward<B>(b)), \
+ expr(std::forward<C>(c)), expr(std::forward<D>(d))}); }
+#define C7(name, type) template <class A, class B, class C, class D, class E, class F, class G> \
+ Term name(A&& a, B&& b, C&& c, D&& d, E&& e, F&& f, G&& g) { return Term(TT::type, std::vector<Term>{ \
+ expr(std::forward<A>(a)), expr(std::forward<B>(b)), expr(std::forward<C>(c)), \
+ expr(std::forward<D>(d)), expr(std::forward<E>(e)), expr(std::forward<F>(f)), \
+ expr(std::forward<G>(g))}); }
+#define C_(name, type, wrap) template <class ...T> Term name(T&& ...a) { \
+ return Term(TT::type, std::vector<Term>{ wrap(expr(std::forward<T>(a)))... }); }
+#define CO1(name, type, wrap) template <class T> Term name(T&& a, OptArgs&& optarg = {}) { \
+ return Term(TT::type, std::vector<Term>{ wrap(expr(std::forward<T>(a)))}, std::move(optarg)); }
+#define CO2(name, type) template <class T, class U> Term name(T&& a, U&& b, OptArgs&& optarg = {}) { \
+ return Term(TT::type, std::vector<Term>{ expr(std::forward<T>(a)), expr(std::forward<U>(b))}, std::move(optarg)); }
+#define func_wrap Term::func_wrap
+
+C1(db_create, DB_CREATE, no_wrap)
+C1(db_drop, DB_DROP, no_wrap)
+C0(db_list)
+CO1(table_create, TABLE_CREATE, no_wrap)
+C1(table_drop, TABLE_DROP, no_wrap)
+C0(table_list)
+C1(db, DB, no_wrap)
+CO1(table, TABLE, no_wrap)
+C_(add, ADD, no_wrap)
+C2(sub, SUB)
+C_(mul, MUL, no_wrap)
+C_(div, DIV, no_wrap)
+C2(mod, MOD)
+C_(and_, AND, no_wrap)
+C_(or_, OR, no_wrap)
+C2(eq, EQ)
+C2(ne, NE)
+C2(gt, GT)
+C2(ge, GE)
+C2(lt, LT)
+C2(le, LE)
+C1(not_, NOT, no_wrap)
+CO0(random)
+CO1(random, RANDOM, no_wrap)
+CO2(random, RANDOM)
+C0(now)
+C4(time, TIME)
+C7(time, TIME)
+C1(epoch_time, EPOCH_TIME, no_wrap)
+CO1(iso8601, ISO8601, no_wrap)
+CO1(js, JAVASCRIPT, no_wrap)
+C1(args, ARGS, no_wrap)
+C_(branch, BRANCH, no_wrap)
+C0(range)
+C1(range, RANGE, no_wrap)
+C2(range, RANGE)
+C0(error)
+C1(error, ERROR, no_wrap)
+C1(json, JSON, no_wrap)
+CO1(http, HTTP, func_wrap)
+C0(uuid)
+C1(uuid, UUID, no_wrap)
+CO2(circle, CIRCLE)
+C1(geojson, GEOJSON, no_wrap)
+C_(line, LINE, no_wrap)
+C2(point, POINT)
+C_(polygon, POLYGON, no_wrap)
+C_(array, MAKE_ARRAY, no_wrap)
+C1(desc, DESC, func_wrap)
+C1(asc, ASC, func_wrap)
+C0(literal)
+C1(literal, LITERAL, no_wrap)
+C1(type_of, TYPE_OF, no_wrap)
+C_(map, MAP, func_wrap)
+C1(floor, FLOOR, no_wrap)
+C1(ceil, CEIL, no_wrap)
+C1(round, ROUND, no_wrap)
+C_(union_, UNION, no_wrap)
+C_(group, GROUP, func_wrap)
+C1(count, COUNT, no_wrap)
+C_(count, COUNT, func_wrap)
+C1(sum, SUM, no_wrap)
+C_(sum, SUM, func_wrap)
+C1(avg, AVG, no_wrap)
+C_(avg, AVG, func_wrap)
+C1(min, MIN, no_wrap)
+C_(min, MIN, func_wrap)
+C1(max, MAX, no_wrap)
+C_(max, MAX, func_wrap)
+C1(distinct, DISTINCT, no_wrap)
+C1(contains, CONTAINS, no_wrap)
+C_(contains, CONTAINS, func_wrap)
+
+#undef C0
+#undef C1
+#undef C2
+#undef C3
+#undef C4
+#undef C7
+#undef C_
+#undef CO1
+#undef CO2
+#undef func_wrap
+
+// $doc(do)
+template <class R, class ...T>
+Term do_(R&& a, T&& ...b) {
+ return expr(std::forward<R>(a)).do_(std::forward<T>(b)...);
+}
+
+// $doc(object)
+template <class ...T>
+Term object(T&& ...a) {
+ return Term::make_object(std::vector<Term>{ expr(std::forward<T>(a))... });
+}
+
+// $doc(binary)
+template <class T>
+Term binary(T&& a) {
+ return Term::make_binary(expr(std::forward<T>(a)));
+}
+
+// Construct an empty optarg
+OptArgs optargs();
+
+// Construct an optarg made out of pairs of arguments
+// For example: optargs("k1", v1, "k2", v2)
+template <class V, class ...T>
+OptArgs optargs(const char* key, V&& val, T&& ...rest) {
+ OptArgs opts = optargs(rest...);
+ opts.emplace(key, expr(std::forward<V>(val)));
+ return opts;
+}
+
+extern Term row;
+extern Term maxval;
+extern Term minval;
+extern Term january;
+extern Term february;
+extern Term march;
+extern Term april;
+extern Term may;
+extern Term june;
+extern Term july;
+extern Term august;
+extern Term september;
+extern Term october;
+extern Term november;
+extern Term december;
+extern Term monday;
+extern Term tuesday;
+extern Term wednesday;
+extern Term thursday;
+extern Term friday;
+extern Term saturday;
+extern Term sunday;
+}
diff --git a/ext/librethinkdbxx/src/types.cc b/ext/librethinkdbxx/src/types.cc
new file mode 100644
index 00000000..ea9becaf
--- /dev/null
+++ b/ext/librethinkdbxx/src/types.cc
@@ -0,0 +1,47 @@
+#include <cstdlib>
+
+#include "types.h"
+#include "error.h"
+
+namespace RethinkDB {
+
+bool Time::parse_utc_offset(const std::string& string, double* offset) {
+ const char *s = string.c_str();
+ double sign = 1;
+ switch (s[0]) {
+ case '-':
+ sign = -1;
+ case '+':
+ ++s;
+ break;
+ case 0:
+ return false;
+ }
+ for (int i = 0; i < 5; ++i) {
+ if (s[i] == 0) return false;
+ if (i == 2) continue;
+ if (s[i] < '0' || s[i] > '9') return false;
+ }
+ if (s[2] != ':') return false;
+ *offset = sign * ((s[0] - '0') * 36000 + (s[1] - '0') * 3600 + (s[3] - '0') * 600 + (s[4] - '0') * 60);
+ return true;
+}
+
+double Time::parse_utc_offset(const std::string& string) {
+ double out;
+ if (!parse_utc_offset(string, &out)) {
+ throw Error("invalid utc offset `%s'", string.c_str());
+ }
+ return out;
+}
+
+std::string Time::utc_offset_string(double offset) {
+ char buf[8];
+ int hour = offset / 3600;
+ int minutes = std::abs(static_cast<int>(offset / 60)) % 60;
+ int n = snprintf(buf, 7, "%+03d:%02d", hour, minutes);
+ buf[n] = 0;
+ return std::string(buf);
+}
+
+}
diff --git a/ext/librethinkdbxx/src/types.h b/ext/librethinkdbxx/src/types.h
new file mode 100644
index 00000000..ac35a871
--- /dev/null
+++ b/ext/librethinkdbxx/src/types.h
@@ -0,0 +1,53 @@
+#pragma once
+
+#include <vector>
+#include <map>
+#include <ctime>
+#include <string>
+
+namespace RethinkDB {
+
+class Datum;
+
+// Represents a null datum
+struct Nil { };
+
+using Array = std::vector<Datum>;
+using Object = std::map<std::string, Datum>;
+
+// Represents a string of bytes. Plain std::strings are passed on to the server as utf-8 strings
+struct Binary {
+ bool operator== (const Binary& other) const {
+ return data == other.data;
+ }
+
+ Binary(const std::string& data_) : data(data_) { }
+ Binary(std::string&& data_) : data(std::move(data_)) { }
+ std::string data;
+};
+
+// Represents a point in time as
+// * A floating amount of seconds since the UNIX epoch
+// * And a timezone offset represented as seconds relative to UTC
+struct Time {
+ Time(double epoch_time_, double utc_offset_ = 0) :
+ epoch_time(epoch_time_), utc_offset(utc_offset_) { }
+
+ static Time now() {
+ return Time(time(NULL));
+ }
+
+ static bool parse_utc_offset(const std::string&, double*);
+ static double parse_utc_offset(const std::string&);
+ static std::string utc_offset_string(double);
+
+ double epoch_time;
+ double utc_offset;
+};
+
+// Not implemented
+class Point;
+class Line;
+class Polygon;
+
+}
diff --git a/ext/librethinkdbxx/src/utils.cc b/ext/librethinkdbxx/src/utils.cc
new file mode 100644
index 00000000..5a2c244d
--- /dev/null
+++ b/ext/librethinkdbxx/src/utils.cc
@@ -0,0 +1,153 @@
+#include "utils.h"
+#include "error.h"
+
+namespace RethinkDB {
+
+size_t utf8_encode(unsigned int code, char* buf) {
+ if (!(code & ~0x7F)) {
+ buf[0] = code;
+ return 1;
+ } else if (!(code & ~0x7FF)) {
+ buf[0] = 0xC0 | (code >> 6);
+ buf[1] = 0x80 | (code & 0x3F);
+ return 2;
+ } else if (!(code & ~0xFFFF)) {
+ buf[0] = 0xE0 | (code >> 12);
+ buf[1] = 0x80 | ((code >> 6) & 0x3F);
+ buf[2] = 0x80 | (code & 0x3F);
+ return 3;
+ } else if (!(code & ~0x1FFFFF)) {
+ buf[0] = 0xF0 | (code >> 18);
+ buf[1] = 0x80 | ((code >> 12) & 0x3F);
+ buf[2] = 0x80 | ((code >> 6) & 0x3F);
+ buf[3] = 0x80 | (code & 0x3F);
+ return 4;
+ } else if (!(code & ~0x3FFFFFF)) {
+ buf[0] = 0xF8 | (code >> 24);
+ buf[1] = 0x80 | ((code >> 18) & 0x3F);
+ buf[2] = 0x80 | ((code >> 12) & 0x3F);
+ buf[3] = 0x80 | ((code >> 6) & 0x3F);
+ buf[4] = 0x80 | (code & 0x3F);
+ return 5;
+ } else if (!(code & ~0x7FFFFFFF)) {
+ buf[0] = 0xFC | (code >> 30);
+ buf[1] = 0x80 | ((code >> 24) & 0x3F);
+ buf[2] = 0x80 | ((code >> 18) & 0x3F);
+ buf[3] = 0x80 | ((code >> 12) & 0x3F);
+ buf[4] = 0x80 | ((code >> 6) & 0x3F);
+ buf[5] = 0x80 | (code & 0x3F);
+ return 6;
+ } else {
+ throw Error("Invalid unicode codepoint %ud", code);
+ }
+}
+
+bool base64_decode(char c, int* out) {
+ if (c >= 'A' && c <= 'Z') {
+ *out = c - 'A';
+ } else if (c >= 'a' && c <= 'z') {
+ *out = c - ('a' - 26);
+ } else if (c >= '0' && c <= '9') {
+ *out = c - ('0' - 52);
+ } else if (c == '+') {
+ *out = 62;
+ } else if (c == '/') {
+ *out = 63;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool base64_decode(const std::string& in, std::string& out) {
+ out.clear();
+ out.reserve(in.size() * 3 / 4);
+ auto read = in.begin();
+ while (true) {
+ int c[4];
+ int end = 4;
+ for (int i = 0; i < 4; i++) {
+ while (true) {
+ if (read == in.end()) {
+ c[i] = 0;
+ end = i;
+ i = 3;
+ break;
+ } else if (base64_decode(*read, &c[i])) {
+ ++read;
+ break;
+ } else {
+ ++read;
+ }
+ }
+ }
+ if (end == 1) return false;
+ int val = c[0] << 18 | c[1] << 12 | c[2] << 6 | c[3];
+ if (end > 1) out.append(1, val >> 16);
+ if (end > 2) out.append(1, val >> 8 & 0xFF);
+ if (end > 3) out.append(1, val & 0xFF);
+ if (end != 4) break;
+ }
+ return true;
+}
+
+char base64_encode(unsigned int c) {
+ if (c < 26) {
+ return 'A' + c;
+ } else if (c < 52) {
+ return 'a' + c - 26;
+ } else if (c < 62) {
+ return '0' + c - 52;
+ } else if (c == 62) {
+ return '+';
+ } else if (c == 63) {
+ return '/';
+ } else {
+ throw Error("unreachable: base64 encoding %d", c);
+ }
+}
+
+void base64_encode(unsigned int* c, int n, std::string& out) {
+ if (n == 0) {
+ return;
+ }
+ out.append(1, base64_encode(c[0] >> 2));
+ out.append(1, base64_encode((c[0] & 0x3) << 4 | c[1] >> 4));
+ if (n == 1) {
+ out.append("==");
+ return;
+ }
+ out.append(1, base64_encode((c[1] & 0xF) << 2 | c[2] >> 6));
+ if (n == 2) {
+ out.append("=");
+ return;
+ }
+ out.append(1, base64_encode(c[2] & 0x3F));
+}
+
+std::string base64_encode(const std::string& in) {
+ std::string out;
+ out.reserve(in.size() * 4 / 3 + in.size() / 48 + 3);
+ auto read = in.begin();
+ while (true) {
+ for (int group = 0; group < 16; ++group) {
+ unsigned int c[3];
+ int i = 0;
+ for (; i < 3; ++i) {
+ if (read == in.end()) {
+ c[i] = 0;
+ break;
+ } else {
+ c[i] = static_cast<unsigned char>(*read++);
+ }
+ }
+ base64_encode(c, i, out);
+ if (i != 3) {
+ return out;
+ }
+ }
+ out.append("\n");
+ }
+}
+
+}
diff --git a/ext/librethinkdbxx/src/utils.h b/ext/librethinkdbxx/src/utils.h
new file mode 100644
index 00000000..04496e2c
--- /dev/null
+++ b/ext/librethinkdbxx/src/utils.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <cstddef>
+#include <string>
+
+namespace RethinkDB {
+
+// The size of the longest UTF-8 encoded unicode codepoint
+const size_t max_utf8_encoded_size = 6;
+
+// Decode a base64 string. Returns false on failure.
+bool base64_decode(const std::string& in, std::string& out);
+std::string base64_encode(const std::string&);
+
+// Encodes a single unicode codepoint into UTF-8. Returns the number of bytes written.
+// Does not add a trailing null byte
+size_t utf8_encode(unsigned int, char*);
+
+}
diff --git a/ext/miniupnpc/Changelog.txt b/ext/miniupnpc/Changelog.txt
index 078bebce..37562c6a 100644
--- a/ext/miniupnpc/Changelog.txt
+++ b/ext/miniupnpc/Changelog.txt
@@ -1,6 +1,20 @@
-$Id: Changelog.txt,v 1.223 2016/04/19 21:06:20 nanard Exp $
+$Id: Changelog.txt,v 1.229 2017/12/12 11:26:25 nanard Exp $
miniUPnP client Changelog.
+2017/12/11:
+ Fix buffer over run in minixml.c
+ Fix uninitialized variable access in upnpreplyparse.c
+
+2017/05/05:
+ Fix CVE-2017-8798 Thanks to tin/Team OSTStrom
+
+2016/11/11:
+ check strlen before memcmp in XML parsing portlistingparse.c
+ fix build under SOLARIS and CYGWIN
+
+2016/10/11:
+ Add python 3 compatibility to IGD test
+
VERSION 2.0 : released 2016/04/19
2016/01/24:
diff --git a/ext/miniupnpc/LICENSE b/ext/miniupnpc/LICENSE
index cb5a0604..08167337 100644
--- a/ext/miniupnpc/LICENSE
+++ b/ext/miniupnpc/LICENSE
@@ -1,5 +1,5 @@
MiniUPnPc
-Copyright (c) 2005-2015, Thomas BERNARD
+Copyright (c) 2005-2016, Thomas BERNARD
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/ext/miniupnpc/MANIFEST.in b/ext/miniupnpc/MANIFEST.in
deleted file mode 100644
index 54b86f95..00000000
--- a/ext/miniupnpc/MANIFEST.in
+++ /dev/null
@@ -1,5 +0,0 @@
-include README
-include miniupnpcmodule.c
-include setup.py
-include *.h
-include libminiupnpc.a
diff --git a/ext/miniupnpc/README b/ext/miniupnpc/README
index 91535dbc..0d3b8054 100644
--- a/ext/miniupnpc/README
+++ b/ext/miniupnpc/README
@@ -1,9 +1,8 @@
Project: miniupnp
-Project web page: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+Project web page: http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
github: https://github.com/miniupnp/miniupnp
-freecode: http://freecode.com/projects/miniupnp
Author: Thomas Bernard
-Copyright (c) 2005-2016 Thomas Bernard
+Copyright (c) 2005-2017 Thomas Bernard
This software is subject to the conditions detailed in the
LICENSE file provided within this distribution.
@@ -58,7 +57,7 @@ If you are using libminiupnpc in your application, please
send me an email !
For any question, you can use the web forum :
-http://miniupnp.tuxfamily.org/forum/
+https://miniupnp.tuxfamily.org/forum/
Bugs should be reported on github :
https://github.com/miniupnp/miniupnp/issues
diff --git a/ext/miniupnpc/apiversions.txt b/ext/miniupnpc/apiversions.txt
deleted file mode 100644
index 9464a867..00000000
--- a/ext/miniupnpc/apiversions.txt
+++ /dev/null
@@ -1,172 +0,0 @@
-$Id: apiversions.txt,v 1.9 2016/01/24 17:24:36 nanard Exp $
-
-Differences in API between miniUPnPc versions
-
-API version 16
- added "status_code" argument to getHTTPResponse(), miniwget() and miniwget_getaddr()
- updated macro :
- #define MINIUPNPC_API_VERSION 16
-
-API version 15
- changed "sameport" argument of upnpDiscover() upnpDiscoverAll() upnpDiscoverDevice()
- to "localport". When 0 or 1, behaviour is not changed, but it can take
- any other value between 2 and 65535
- Existing programs should be compatible
- updated macro :
- #define MINIUPNPC_API_VERSION 15
-
-API version 14
-miniupnpc.h
- add ttl argument to upnpDiscover() upnpDiscoverAll() upnpDiscoverDevice()
- upnpDiscoverDevices()
- getDevicesFromMiniSSDPD() :
- connectToMiniSSDPD() / disconnectFromMiniSSDPD()
- requestDevicesFromMiniSSDPD() / receiveDevicesFromMiniSSDPD()
- updated macro :
- #define MINIUPNPC_API_VERSION 14
-
-API version 13
-miniupnpc.h:
- add searchalltype param to upnpDiscoverDevices() function
- updated macro :
- #define MINIUPNPC_API_VERSION 13
-
-API version 12
-miniupnpc.h :
- add upnpDiscoverAll() / upnpDiscoverDevice() / upnpDiscoverDevices()
- functions
- updated macros :
- #define MINIUPNPC_API_VERSION 12
-
-API version 11
-
-upnpreplyparse.h / portlistingparse.h :
- removed usage of sys/queue.h / bsdqueue.h
-
-miniupnpc.h:
- updated macros :
- #define MINIUPNPC_API_VERSION 11
-
-====================== miniUPnPc version 1.9 ======================
-API version 10
-
-upnpcommands.h:
- added argument remoteHost to UPNP_GetSpecificPortMappingEntry()
-
-miniupnpc.h:
- updated macros :
- #define MINIUPNPC_VERSION "1.9"
- #define MINIUPNPC_API_VERSION 10
-
-====================== miniUPnPc version 1.8 ======================
-API version 9
-
-miniupnpc.h:
- updated macros :
- #define MINIUPNPC_VERSION "1.8"
- #define MINIUPNPC_API_VERSION 9
- added "unsigned int scope_id;" to struct UPNPDev
- added scope_id argument to GetUPNPUrls()
-
-
-
-====================== miniUPnPc version 1.7 ======================
-API version 8
-
-miniupnpc.h :
- add new macros :
- #define MINIUPNPC_VERSION "1.7"
- #define MINIUPNPC_API_VERSION 8
- add rootdescURL to struct UPNPUrls
-
-
-
-====================== miniUPnPc version 1.6 ======================
-API version 8
-
-Adding support for IPv6.
-igd_desc_parse.h :
- struct IGDdatas_service :
- add char presentationurl[MINIUPNPC_URL_MAXSIZE];
- struct IGDdatas :
- add struct IGDdatas_service IPv6FC;
-miniupnpc.h :
- new macros :
- #define UPNPDISCOVER_SUCCESS (0)
- #define UPNPDISCOVER_UNKNOWN_ERROR (-1)
- #define UPNPDISCOVER_SOCKET_ERROR (-101)
- #define UPNPDISCOVER_MEMORY_ERROR (-102)
- simpleUPnPcommand() prototype changed (but is normaly not used by API users)
- add arguments ipv6 and error to upnpDiscover() :
- struct UPNPDev *
- upnpDiscover(int delay, const char * multicastif,
- const char * minissdpdsock, int sameport,
- int ipv6,
- int * error);
- add controlURL_6FC member to struct UPNPUrls :
- struct UPNPUrls {
- char * controlURL;
- char * ipcondescURL;
- char * controlURL_CIF;
- char * controlURL_6FC;
- };
-
-upnpcommands.h :
- add leaseDuration argument to UPNP_AddPortMapping()
- add desc, enabled and leaseDuration arguments to UPNP_GetSpecificPortMappingEntry()
- add UPNP_GetListOfPortMappings() function (IGDv2)
- add IGDv2 IPv6 related functions :
- UPNP_GetFirewallStatus()
- UPNP_GetOutboundPinholeTimeout()
- UPNP_AddPinhole()
- UPNP_UpdatePinhole()
- UPNP_DeletePinhole()
- UPNP_CheckPinholeWorking()
- UPNP_GetPinholePackets()
-
-
-
-====================== miniUPnPc version 1.5 ======================
-API version 5
-
-new function :
-int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *);
-new macro in upnpcommands.h :
-#define UPNPCOMMAND_HTTP_ERROR
-
-====================== miniUPnPc version 1.4 ======================
-Same API as version 1.3
-
-====================== miniUPnPc version 1.3 ======================
-API version 4
-
-Use UNSIGNED_INTEGER type for
-UPNP_GetTotalBytesSent(), UPNP_GetTotalBytesReceived(),
-UPNP_GetTotalPacketsSent(), UPNP_GetTotalPacketsReceived()
-Add remoteHost argument to UPNP_AddPortMapping() and UPNP_DeletePortMapping()
-
-====================== miniUPnPc version 1.2 ======================
-API version 3
-
-added sameport argument to upnpDiscover()
-struct UPNPDev *
-upnpDiscover(int delay, const char * multicastif,
- const char * minissdpdsock, int sameport);
-
-====================== miniUPnPc Version 1.1 ======================
-Same API as 1.0
-
-
-====================== miniUPnPc Version 1.0 ======================
-API version 2
-
-
-struct UPNPDev {
- struct UPNPDev * pNext;
- char * descURL;
- char * st;
- char buffer[2];
-};
-struct UPNPDev * upnpDiscover(int delay, const char * multicastif,
- const char * minissdpdsock);
-
diff --git a/ext/miniupnpc/connecthostport.c b/ext/miniupnpc/connecthostport.c
index c12d7bdd..aed62c76 100644
--- a/ext/miniupnpc/connecthostport.c
+++ b/ext/miniupnpc/connecthostport.c
@@ -1,7 +1,7 @@
-/* $Id: connecthostport.c,v 1.16 2016/12/16 08:57:53 nanard Exp $ */
+/* $Id: connecthostport.c,v 1.17 2017/04/21 09:58:30 nanard Exp $ */
/* Project : miniupnp
* Author : Thomas Bernard
- * Copyright (c) 2010-2016 Thomas Bernard
+ * Copyright (c) 2010-2017 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
@@ -36,15 +36,13 @@
/* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions
* during the connect() call */
#define MINIUPNPC_IGNORE_EINTR
-#ifndef USE_GETHOSTBYNAME
#include <sys/socket.h>
#include <sys/select.h>
-#endif /* #ifndef USE_GETHOSTBYNAME */
#endif /* #else _WIN32 */
/* definition of PRINT_SOCKET_ERROR */
#ifdef _WIN32
-#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#define PRINT_SOCKET_ERROR(x) fprintf(stderr, "Socket error: %s, %d\n", x, WSAGetLastError());
#else
#define PRINT_SOCKET_ERROR(x) perror(x)
#endif
diff --git a/ext/miniupnpc/external-ip.sh b/ext/miniupnpc/external-ip.sh
deleted file mode 100755
index 965d86b2..00000000
--- a/ext/miniupnpc/external-ip.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-# $Id: external-ip.sh,v 1.1 2010/08/05 12:57:41 nanard Exp $
-# (c) 2010 Reuben Hawkins
-upnpc -s | grep ExternalIPAddress | sed 's/[^0-9\.]//g'
diff --git a/ext/miniupnpc/minihttptestserver.c b/ext/miniupnpc/minihttptestserver.c
deleted file mode 100644
index d95dd7c9..00000000
--- a/ext/miniupnpc/minihttptestserver.c
+++ /dev/null
@@ -1,659 +0,0 @@
-/* $Id: minihttptestserver.c,v 1.20 2016/12/16 08:54:55 nanard Exp $ */
-/* Project : miniUPnP
- * Author : Thomas Bernard
- * Copyright (c) 2011-2016 Thomas Bernard
- * This software is subject to the conditions detailed in the
- * LICENCE file provided in this distribution.
- * */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <signal.h>
-#include <time.h>
-#include <errno.h>
-
-#ifndef INADDR_LOOPBACK
-#define INADDR_LOOPBACK 0x7f000001
-#endif
-
-#define CRAP_LENGTH (2048)
-
-volatile sig_atomic_t quit = 0;
-volatile sig_atomic_t child_to_wait_for = 0;
-
-/**
- * signal handler for SIGCHLD (child status has changed)
- */
-void handle_signal_chld(int sig)
-{
- (void)sig;
- /* printf("handle_signal_chld(%d)\n", sig); */
- ++child_to_wait_for;
-}
-
-/**
- * signal handler for SIGINT (CRTL C)
- */
-void handle_signal_int(int sig)
-{
- (void)sig;
- /* printf("handle_signal_int(%d)\n", sig); */
- quit = 1;
-}
-
-/**
- * build a text/plain content of the specified length
- */
-void build_content(char * p, int n)
-{
- char line_buffer[80];
- int k;
- int i = 0;
-
- while(n > 0) {
- k = snprintf(line_buffer, sizeof(line_buffer),
- "%04d_ABCDEFGHIJKL_This_line_is_64_bytes_long_ABCDEFGHIJKL_%04d\r\n",
- i, i);
- if(k != 64) {
- fprintf(stderr, "snprintf() returned %d in build_content()\n", k);
- }
- ++i;
- if(n >= 64) {
- memcpy(p, line_buffer, 64);
- p += 64;
- n -= 64;
- } else {
- memcpy(p, line_buffer, n);
- p += n;
- n = 0;
- }
- }
-}
-
-/**
- * build crappy content
- */
-void build_crap(char * p, int n)
-{
- static const char crap[] = "_CRAP_\r\n";
- int i;
-
- while(n > 0) {
- i = sizeof(crap) - 1;
- if(i > n)
- i = n;
- memcpy(p, crap, i);
- p += i;
- n -= i;
- }
-}
-
-/**
- * build chunked response.
- * return a malloc'ed buffer
- */
-char * build_chunked_response(int content_length, int * response_len)
-{
- char * response_buffer;
- char * content_buffer;
- int buffer_length;
- int i, n;
-
- /* allocate to have some margin */
- buffer_length = 256 + content_length + (content_length >> 4);
- response_buffer = malloc(buffer_length);
- if(response_buffer == NULL)
- return NULL;
- *response_len = snprintf(response_buffer, buffer_length,
- "HTTP/1.1 200 OK\r\n"
- "Content-Type: text/plain\r\n"
- "Transfer-Encoding: chunked\r\n"
- "\r\n");
-
- /* build the content */
- content_buffer = malloc(content_length);
- if(content_buffer == NULL) {
- free(response_buffer);
- return NULL;
- }
- build_content(content_buffer, content_length);
-
- /* chunk it */
- i = 0;
- while(i < content_length) {
- n = (rand() % 199) + 1;
- if(i + n > content_length) {
- n = content_length - i;
- }
- /* TODO : check buffer size ! */
- *response_len += snprintf(response_buffer + *response_len,
- buffer_length - *response_len,
- "%x\r\n", n);
- memcpy(response_buffer + *response_len, content_buffer + i, n);
- *response_len += n;
- i += n;
- response_buffer[(*response_len)++] = '\r';
- response_buffer[(*response_len)++] = '\n';
- }
- /* the last chunk : "0\r\n" a empty body and then
- * the final "\r\n" */
- memcpy(response_buffer + *response_len, "0\r\n\r\n", 5);
- *response_len += 5;
- free(content_buffer);
-
- printf("resp_length=%d buffer_length=%d content_length=%d\n",
- *response_len, buffer_length, content_length);
- return response_buffer;
-}
-
-/* favicon.ico generator */
-#ifdef OLD_HEADER
-#define FAVICON_LENGTH (6 + 16 + 12 + 8 + 32 * 4)
-#else
-#define FAVICON_LENGTH (6 + 16 + 40 + 8 + 32 * 4)
-#endif
-void build_favicon_content(char * p, int n)
-{
- int i;
- if(n < FAVICON_LENGTH)
- return;
- /* header : 6 bytes */
- *p++ = 0;
- *p++ = 0;
- *p++ = 1; /* type : ICO */
- *p++ = 0;
- *p++ = 1; /* number of images in file */
- *p++ = 0;
- /* image directory (1 entry) : 16 bytes */
- *p++ = 16; /* width */
- *p++ = 16; /* height */
- *p++ = 2; /* number of colors in the palette. 0 = no palette */
- *p++ = 0; /* reserved */
- *p++ = 1; /* color planes */
- *p++ = 0; /* " */
- *p++ = 1; /* bpp */
- *p++ = 0; /* " */
-#ifdef OLD_HEADER
- *p++ = 12 + 8 + 32 * 4; /* bmp size */
-#else
- *p++ = 40 + 8 + 32 * 4; /* bmp size */
-#endif
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- *p++ = 6 + 16; /* bmp offset */
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- /* BMP */
-#ifdef OLD_HEADER
- /* BITMAPCOREHEADER */
- *p++ = 12; /* size of this header */
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- *p++ = 16; /* width */
- *p++ = 0; /* " */
- *p++ = 16 * 2; /* height x 2 ! */
- *p++ = 0; /* " */
- *p++ = 1; /* color planes */
- *p++ = 0; /* " */
- *p++ = 1; /* bpp */
- *p++ = 0; /* " */
-#else
- /* BITMAPINFOHEADER */
- *p++ = 40; /* size of this header */
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- *p++ = 16; /* width */
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- *p++ = 16 * 2; /* height x 2 ! */
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- *p++ = 1; /* color planes */
- *p++ = 0; /* " */
- *p++ = 1; /* bpp */
- *p++ = 0; /* " */
- /* compression method, image size, ppm x, ppm y */
- /* colors in the palette ? */
- /* important colors */
- for(i = 4 * 6; i > 0; --i)
- *p++ = 0;
-#endif
- /* palette */
- *p++ = 0; /* b */
- *p++ = 0; /* g */
- *p++ = 0; /* r */
- *p++ = 0; /* reserved */
- *p++ = 255; /* b */
- *p++ = 255; /* g */
- *p++ = 255; /* r */
- *p++ = 0; /* reserved */
- /* pixel data */
- for(i = 16; i > 0; --i) {
- if(i & 1) {
- *p++ = 0125;
- *p++ = 0125;
- } else {
- *p++ = 0252;
- *p++ = 0252;
- }
- *p++ = 0;
- *p++ = 0;
- }
- /* Opacity MASK */
- for(i = 16 * 4; i > 0; --i) {
- *p++ = 0;
- }
-}
-
-enum modes {
- MODE_INVALID, MODE_CHUNKED, MODE_ADDCRAP, MODE_NORMAL, MODE_FAVICON
-};
-
-const struct {
- const enum modes mode;
- const char * text;
-} modes_array[] = {
- {MODE_CHUNKED, "chunked"},
- {MODE_ADDCRAP, "addcrap"},
- {MODE_NORMAL, "normal"},
- {MODE_FAVICON, "favicon.ico"},
- {MODE_INVALID, NULL}
-};
-
-/**
- * write the response with random behaviour !
- */
-void send_response(int c, const char * buffer, int len)
-{
- int n;
- while(len > 0) {
- n = (rand() % 99) + 1;
- if(n > len)
- n = len;
- n = write(c, buffer, n);
- if(n < 0) {
- if(errno != EINTR) {
- perror("write");
- return;
- }
- /* if errno == EINTR, try again */
- } else {
- len -= n;
- buffer += n;
- }
- usleep(10000); /* 10ms */
- }
-}
-
-/**
- * handle the HTTP connection
- */
-void handle_http_connection(int c)
-{
- char request_buffer[2048];
- int request_len = 0;
- int headers_found = 0;
- int n, i;
- char request_method[16];
- char request_uri[256];
- char http_version[16];
- char * p;
- char * response_buffer;
- int response_len;
- enum modes mode;
- int content_length = 16*1024;
-
- /* read the request */
- while(request_len < (int)sizeof(request_buffer) && !headers_found) {
- n = read(c,
- request_buffer + request_len,
- sizeof(request_buffer) - request_len);
- if(n < 0) {
- if(errno == EINTR)
- continue;
- perror("read");
- return;
- } else if(n==0) {
- /* remote host closed the connection */
- break;
- } else {
- request_len += n;
- for(i = 0; i < request_len - 3; i++) {
- if(0 == memcmp(request_buffer + i, "\r\n\r\n", 4)) {
- /* found the end of headers */
- headers_found = 1;
- break;
- }
- }
- }
- }
- if(!headers_found) {
- /* error */
- printf("no HTTP header found in the request\n");
- return;
- }
- printf("headers :\n%.*s", request_len, request_buffer);
- /* the request have been received, now parse the request line */
- p = request_buffer;
- for(i = 0; i < (int)sizeof(request_method) - 1; i++) {
- if(*p == ' ' || *p == '\r')
- break;
- request_method[i] = *p;
- ++p;
- }
- request_method[i] = '\0';
- while(*p == ' ')
- p++;
- for(i = 0; i < (int)sizeof(request_uri) - 1; i++) {
- if(*p == ' ' || *p == '\r')
- break;
- request_uri[i] = *p;
- ++p;
- }
- request_uri[i] = '\0';
- while(*p == ' ')
- p++;
- for(i = 0; i < (int)sizeof(http_version) - 1; i++) {
- if(*p == ' ' || *p == '\r')
- break;
- http_version[i] = *p;
- ++p;
- }
- http_version[i] = '\0';
- printf("Method = %s, URI = %s, %s\n",
- request_method, request_uri, http_version);
- /* check if the request method is allowed */
- if(0 != strcmp(request_method, "GET")) {
- const char response405[] = "HTTP/1.1 405 Method Not Allowed\r\n"
- "Allow: GET\r\n\r\n";
- const char * pc;
- /* 405 Method Not Allowed */
- /* The response MUST include an Allow header containing a list
- * of valid methods for the requested resource. */
- n = sizeof(response405) - 1;
- pc = response405;
- while(n > 0) {
- i = write(c, pc, n);
- if(i<0) {
- if(errno != EINTR) {
- perror("write");
- return;
- }
- } else {
- n -= i;
- pc += i;
- }
- }
- return;
- }
-
- mode = MODE_INVALID;
- /* use the request URI to know what to do */
- for(i = 0; modes_array[i].mode != MODE_INVALID; i++) {
- if(strstr(request_uri, modes_array[i].text)) {
- mode = modes_array[i].mode; /* found */
- break;
- }
- }
-
- switch(mode) {
- case MODE_CHUNKED:
- response_buffer = build_chunked_response(content_length, &response_len);
- break;
- case MODE_ADDCRAP:
- response_len = content_length+256;
- response_buffer = malloc(response_len);
- if(!response_buffer)
- break;
- n = snprintf(response_buffer, response_len,
- "HTTP/1.1 200 OK\r\n"
- "Server: minihttptestserver\r\n"
- "Content-Type: text/plain\r\n"
- "Content-Length: %d\r\n"
- "\r\n", content_length);
- response_len = content_length+n+CRAP_LENGTH;
- p = realloc(response_buffer, response_len);
- if(p == NULL) {
- /* error 500 */
- free(response_buffer);
- response_buffer = NULL;
- break;
- }
- response_buffer = p;
- build_content(response_buffer + n, content_length);
- build_crap(response_buffer + n + content_length, CRAP_LENGTH);
- break;
- case MODE_FAVICON:
- content_length = FAVICON_LENGTH;
- response_len = content_length + 256;
- response_buffer = malloc(response_len);
- if(!response_buffer)
- break;
- n = snprintf(response_buffer, response_len,
- "HTTP/1.1 200 OK\r\n"
- "Server: minihttptestserver\r\n"
- "Content-Type: image/vnd.microsoft.icon\r\n"
- "Content-Length: %d\r\n"
- "\r\n", content_length);
- /* image/x-icon */
- build_favicon_content(response_buffer + n, content_length);
- response_len = content_length + n;
- break;
- default:
- response_len = content_length+256;
- response_buffer = malloc(response_len);
- if(!response_buffer)
- break;
- n = snprintf(response_buffer, response_len,
- "HTTP/1.1 200 OK\r\n"
- "Server: minihttptestserver\r\n"
- "Content-Type: text/plain\r\n"
- "\r\n");
- response_len = content_length+n;
- p = realloc(response_buffer, response_len);
- if(p == NULL) {
- /* Error 500 */
- free(response_buffer);
- response_buffer = NULL;
- break;
- }
- response_buffer = p;
- build_content(response_buffer + n, response_len - n);
- }
-
- if(response_buffer) {
- send_response(c, response_buffer, response_len);
- free(response_buffer);
- } else {
- /* Error 500 */
- }
-}
-
-/**
- */
-int main(int argc, char * * argv) {
- int ipv6 = 0;
- int s, c, i;
- unsigned short port = 0;
- struct sockaddr_storage server_addr;
- socklen_t server_addrlen;
- struct sockaddr_storage client_addr;
- socklen_t client_addrlen;
- pid_t pid;
- int child = 0;
- int status;
- const char * expected_file_name = NULL;
- struct sigaction sa;
-
- for(i = 1; i < argc; i++) {
- if(argv[i][0] == '-') {
- switch(argv[i][1]) {
- case '6':
- ipv6 = 1;
- break;
- case 'e':
- /* write expected file ! */
- expected_file_name = argv[++i];
- break;
- case 'p':
- /* port */
- if(++i < argc) {
- port = (unsigned short)atoi(argv[i]);
- }
- break;
- default:
- fprintf(stderr, "unknown command line switch '%s'\n", argv[i]);
- }
- } else {
- fprintf(stderr, "unkown command line argument '%s'\n", argv[i]);
- }
- }
-
- srand(time(NULL));
-
- memset(&sa, 0, sizeof(struct sigaction));
-
- /*signal(SIGCHLD, handle_signal_chld);*/
- sa.sa_handler = handle_signal_chld;
- if(sigaction(SIGCHLD, &sa, NULL) < 0) {
- perror("sigaction");
- return 1;
- }
- /*signal(SIGINT, handle_signal_int);*/
- sa.sa_handler = handle_signal_int;
- if(sigaction(SIGINT, &sa, NULL) < 0) {
- perror("sigaction");
- return 1;
- }
-
- s = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0);
- if(s < 0) {
- perror("socket");
- return 1;
- }
- memset(&server_addr, 0, sizeof(struct sockaddr_storage));
- memset(&client_addr, 0, sizeof(struct sockaddr_storage));
- if(ipv6) {
- struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&server_addr;
- addr->sin6_family = AF_INET6;
- addr->sin6_port = htons(port);
- addr->sin6_addr = in6addr_loopback;
- } else {
- struct sockaddr_in * addr = (struct sockaddr_in *)&server_addr;
- addr->sin_family = AF_INET;
- addr->sin_port = htons(port);
- addr->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- }
- if(bind(s, (struct sockaddr *)&server_addr,
- ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) < 0) {
- perror("bind");
- return 1;
- }
- if(listen(s, 5) < 0) {
- perror("listen");
- }
- if(port == 0) {
- server_addrlen = sizeof(struct sockaddr_storage);
- if(getsockname(s, (struct sockaddr *)&server_addr, &server_addrlen) < 0) {
- perror("getsockname");
- return 1;
- }
- if(ipv6) {
- struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&server_addr;
- port = ntohs(addr->sin6_port);
- } else {
- struct sockaddr_in * addr = (struct sockaddr_in *)&server_addr;
- port = ntohs(addr->sin_port);
- }
- printf("Listening on port %hu\n", port);
- fflush(stdout);
- }
-
- /* write expected file */
- if(expected_file_name) {
- FILE * f;
- f = fopen(expected_file_name, "wb");
- if(f) {
- char * buffer;
- buffer = malloc(16*1024);
- if(buffer == NULL) {
- fprintf(stderr, "memory allocation error\n");
- } else {
- build_content(buffer, 16*1024);
- i = fwrite(buffer, 1, 16*1024, f);
- if(i != 16*1024) {
- fprintf(stderr, "error writing to file %s : %dbytes written (out of %d)\n", expected_file_name, i, 16*1024);
- }
- free(buffer);
- }
- fclose(f);
- } else {
- fprintf(stderr, "error opening file %s for writing\n", expected_file_name);
- }
- }
-
- /* fork() loop */
- while(!child && !quit) {
- while(child_to_wait_for > 0) {
- pid = wait(&status);
- if(pid < 0) {
- perror("wait");
- } else {
- printf("child(%d) terminated with status %d\n", (int)pid, status);
- }
- --child_to_wait_for;
- }
- client_addrlen = sizeof(struct sockaddr_storage);
- c = accept(s, (struct sockaddr *)&client_addr,
- &client_addrlen);
- if(c < 0) {
- if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
- continue;
- perror("accept");
- return 1;
- }
- printf("accept...\n");
- pid = fork();
- if(pid < 0) {
- perror("fork");
- return 1;
- } else if(pid == 0) {
- /* child */
- child = 1;
- close(s);
- s = -1;
- handle_http_connection(c);
- }
- close(c);
- }
- if(s >= 0) {
- close(s);
- s = -1;
- }
- if(!child) {
- while(child_to_wait_for > 0) {
- pid = wait(&status);
- if(pid < 0) {
- perror("wait");
- } else {
- printf("child(%d) terminated with status %d\n", (int)pid, status);
- }
- --child_to_wait_for;
- }
- printf("Bye...\n");
- }
- return 0;
-}
-
diff --git a/ext/miniupnpc/minisoap.c b/ext/miniupnpc/minisoap.c
index 7aa0213e..5b8c0784 100644
--- a/ext/miniupnpc/minisoap.c
+++ b/ext/miniupnpc/minisoap.c
@@ -1,4 +1,4 @@
-/* $Id: minisoap.c,v 1.24 2015/10/26 17:05:07 nanard Exp $ */
+/* $Id: minisoap.c,v 1.25 2017/04/21 10:03:24 nanard Exp $ */
/* Project : miniupnp
* Author : Thomas Bernard
* Copyright (c) 2005-2015 Thomas Bernard
@@ -26,11 +26,17 @@
#define UPNP_VERSION_STRING "UPnP/1.1"
#endif
+#ifdef __ANDROID__
+#define OS_STRING "Android"
+#define MINIUPNPC_VERSION_STRING "2.0"
+#define UPNP_VERSION_STRING "UPnP/1.1"
+#endif
+
/* only for malloc */
#include <stdlib.h>
#ifdef _WIN32
-#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#define PRINT_SOCKET_ERROR(x) fprintf(stderr, "Socket error: %s, %d\n", x, WSAGetLastError());
#else
#define PRINT_SOCKET_ERROR(x) perror(x)
#endif
diff --git a/ext/miniupnpc/minissdpc.c b/ext/miniupnpc/minissdpc.c
index 06b11e80..3479de8e 100644
--- a/ext/miniupnpc/minissdpc.c
+++ b/ext/miniupnpc/minissdpc.c
@@ -1,9 +1,9 @@
-/* $Id: minissdpc.c,v 1.33 2016/12/16 08:57:20 nanard Exp $ */
+/* $Id: minissdpc.c,v 1.35 2017/11/02 15:34:36 nanard Exp $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project : miniupnp
* Web : http://miniupnp.free.fr/
* Author : Thomas BERNARD
- * copyright (c) 2005-2016 Thomas Bernard
+ * copyright (c) 2005-2017 Thomas Bernard
* This software is subjet to the conditions detailed in the
* provided LICENCE file. */
/*#include <syslog.h>*/
@@ -62,7 +62,7 @@ struct sockaddr_un {
#endif
#ifdef _WIN32
-#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#define PRINT_SOCKET_ERROR(x) fprintf(stderr, "Socket error: %s, %d\n", x, WSAGetLastError());
#else
#define PRINT_SOCKET_ERROR(x) perror(x)
#endif
@@ -201,6 +201,7 @@ connectToMiniSSDPD(const char * socketpath)
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
if(!socketpath)
socketpath = "/var/run/minissdpd.sock";
+ memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socketpath, sizeof(addr.sun_path));
/* TODO : check if we need to handle the EINTR */
@@ -502,6 +503,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
unsigned long _ttl = (unsigned long)ttl;
#endif
int linklocal = 1;
+ int sentok;
if(error)
*error = MINISSDPC_UNKNOWN_ERROR;
@@ -612,14 +614,27 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
return NULL;
}
+ if(ipv6) {
#ifdef _WIN32
- if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&_ttl, sizeof(_ttl)) < 0)
+ DWORD mcastHops = ttl;
+ if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char *)&mcastHops, sizeof(mcastHops)) < 0)
#else /* _WIN32 */
- if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
+ int mcastHops = ttl;
+ if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &mcastHops, sizeof(mcastHops)) < 0)
#endif /* _WIN32 */
- {
- /* not a fatal error */
- PRINT_SOCKET_ERROR("setsockopt(IP_MULTICAST_TTL,...)");
+ {
+ PRINT_SOCKET_ERROR("setsockopt(IPV6_MULTICAST_HOPS,...)");
+ }
+ } else {
+#ifdef _WIN32
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&_ttl, sizeof(_ttl)) < 0)
+#else /* _WIN32 */
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
+#endif /* _WIN32 */
+ {
+ /* not a fatal error */
+ PRINT_SOCKET_ERROR("setsockopt(IP_MULTICAST_TTL,...)");
+ }
}
if(multicastif)
@@ -704,6 +719,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
}
/* receiving SSDP response packet */
for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) {
+ sentok = 0;
/* sending the SSDP M-SEARCH packet */
n = snprintf(bufr, sizeof(bufr),
MSearchMsgFmt,
@@ -747,7 +763,8 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
if(error)
*error = MINISSDPC_SOCKET_ERROR;
PRINT_SOCKET_ERROR("sendto");
- break;
+ } else {
+ sentok = 1;
}
#else /* #ifdef NO_GETADDRINFO */
memset(&hints, 0, sizeof(hints));
@@ -779,19 +796,20 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
#endif
PRINT_SOCKET_ERROR("sendto");
continue;
+ } else {
+ sentok = 1;
}
}
freeaddrinfo(servinfo);
- if(n < 0) {
+ if(!sentok) {
if(error)
*error = MINISSDPC_SOCKET_ERROR;
- break;
}
#endif /* #ifdef NO_GETADDRINFO */
/* Waiting for SSDP REPLY packet to M-SEARCH
* if searchalltypes is set, enter the loop only
* when the last deviceType is reached */
- if(!searchalltypes || !deviceTypes[deviceIndex + 1]) do {
+ if((sentok && !searchalltypes) || !deviceTypes[deviceIndex + 1]) do {
n = receivedata(sudp, bufr, sizeof(bufr), delay, &scope_id);
if (n < 0) {
/* error */
diff --git a/ext/miniupnpc/miniupnpc.h b/ext/miniupnpc/miniupnpc.h
index 4cc45f73..4a805b11 100644
--- a/ext/miniupnpc/miniupnpc.h
+++ b/ext/miniupnpc/miniupnpc.h
@@ -19,7 +19,7 @@
#define UPNPDISCOVER_MEMORY_ERROR (-102)
/* versions : */
-#define MINIUPNPC_VERSION "2.0.20161216"
+#define MINIUPNPC_VERSION "2.0.20171212"
#define MINIUPNPC_API_VERSION 16
/* Source port:
diff --git a/ext/miniupnpc/miniupnpcmodule.c b/ext/miniupnpc/miniupnpcmodule.c
index a5bdce44..bbff0738 100644
--- a/ext/miniupnpc/miniupnpcmodule.c
+++ b/ext/miniupnpc/miniupnpcmodule.c
@@ -1,8 +1,8 @@
-/* $Id: miniupnpcmodule.c,v 1.29 2015/10/26 17:01:30 nanard Exp $*/
+/* $Id: miniupnpcmodule.c,v 1.31 2017/11/02 15:37:28 nanard Exp $*/
/* Project : miniupnp
* Author : Thomas BERNARD
* website : http://miniupnp.tuxfamily.org/
- * copyright (c) 2007-2014 Thomas Bernard
+ * copyright (c) 2007-2016 Thomas Bernard
* This software is subjet to the conditions detailed in the
* provided LICENCE file. */
#include <Python.h>
@@ -12,6 +12,10 @@
#include "upnpcommands.h"
#include "upnperrors.h"
+#ifdef _WIN32
+#include <winsock2.h>
+#endif
+
/* for compatibility with Python < 2.4 */
#ifndef Py_RETURN_NONE
#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
@@ -303,7 +307,7 @@ UPnP_addportmapping(UPnPObject *self, PyObject *args)
const char * remoteHost;
const char * leaseDuration = "0";
int r;
- if (!PyArg_ParseTuple(args, "HssHss", &ePort, &proto,
+ if (!PyArg_ParseTuple(args, "HssHzz", &ePort, &proto,
&host, &iPort, &desc, &remoteHost))
return NULL;
Py_BEGIN_ALLOW_THREADS
@@ -345,7 +349,7 @@ UPnP_addanyportmapping(UPnPObject *self, PyObject *args)
const char * remoteHost;
const char * leaseDuration = "0";
int r;
- if (!PyArg_ParseTuple(args, "HssHss", &ePort, &proto, &host, &iPort, &desc, &remoteHost))
+ if (!PyArg_ParseTuple(args, "HssHzz", &ePort, &proto, &host, &iPort, &desc, &remoteHost))
return NULL;
Py_BEGIN_ALLOW_THREADS
sprintf(extPort, "%hu", ePort);
@@ -669,6 +673,10 @@ initminiupnpc(void)
PyObject* m;
#ifdef _WIN32
+ /* initialize Winsock. */
+ WSADATA wsaData;
+ int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
+
UPnPType.tp_new = PyType_GenericNew;
#endif
if (PyType_Ready(&UPnPType) < 0)
diff --git a/ext/miniupnpc/miniwget.c b/ext/miniupnpc/miniwget.c
index 93c8aa6b..e23f11e3 100644
--- a/ext/miniupnpc/miniwget.c
+++ b/ext/miniupnpc/miniwget.c
@@ -1,8 +1,8 @@
-/* $Id: miniwget.c,v 1.76 2016/12/16 08:54:04 nanard Exp $ */
+/* $Id: miniwget.c,v 1.77 2017/05/09 10:04:57 nanard Exp $ */
/* Project : miniupnp
* Website : http://miniupnp.free.fr/
* Author : Thomas Bernard
- * Copyright (c) 2005-2016 Thomas Bernard
+ * Copyright (c) 2005-2017 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
@@ -48,13 +48,18 @@
#define MIN(x,y) (((x)<(y))?(x):(y))
#endif /* MIN */
-
#ifdef _WIN32
#define OS_STRING "Win32"
#define MINIUPNPC_VERSION_STRING "2.0"
#define UPNP_VERSION_STRING "UPnP/1.1"
#endif
+#ifdef __ANDROID__
+#define OS_STRING "Android"
+#define MINIUPNPC_VERSION_STRING "2.0"
+#define UPNP_VERSION_STRING "UPnP/1.1"
+#endif
+
#include "miniwget.h"
#include "connecthostport.h"
#include "receivedata.h"
@@ -116,7 +121,7 @@ getHTTPResponse(int s, int * size, int * status_code)
chunksize_buf[0] = '\0';
chunksize_buf_index = 0;
- while((n = receivedata(s, buf, 2048, 5000, NULL)) > 0)
+ while((n = receivedata(s, buf, sizeof(buf), 5000, NULL)) > 0)
{
if(endofheaders == 0)
{
@@ -289,11 +294,12 @@ getHTTPResponse(int s, int * size, int * status_code)
goto end_of_stream;
}
}
- bytestocopy = ((int)chunksize < (n - i))?chunksize:(unsigned int)(n - i);
+ /* it is guaranteed that (n >= i) */
+ bytestocopy = (chunksize < (unsigned int)(n - i))?chunksize:(unsigned int)(n - i);
if((content_buf_used + bytestocopy) > content_buf_len)
{
char * tmp;
- if(content_length >= (int)(content_buf_used + bytestocopy)) {
+ if((content_length >= 0) && ((unsigned int)content_length >= (content_buf_used + bytestocopy))) {
content_buf_len = content_length;
} else {
content_buf_len = content_buf_used + bytestocopy;
@@ -318,14 +324,15 @@ getHTTPResponse(int s, int * size, int * status_code)
{
/* not chunked */
if(content_length > 0
- && (int)(content_buf_used + n) > content_length) {
+ && (content_buf_used + n) > (unsigned int)content_length) {
/* skipping additional bytes */
n = content_length - content_buf_used;
}
if(content_buf_used + n > content_buf_len)
{
char * tmp;
- if(content_length >= (int)(content_buf_used + n)) {
+ if(content_length >= 0
+ && (unsigned int)content_length >= (content_buf_used + n)) {
content_buf_len = content_length;
} else {
content_buf_len = content_buf_used + n;
@@ -345,7 +352,7 @@ getHTTPResponse(int s, int * size, int * status_code)
}
}
/* use the Content-Length header value if available */
- if(content_length > 0 && (int)content_buf_used >= content_length)
+ if(content_length > 0 && content_buf_used >= (unsigned int)content_length)
{
#ifdef DEBUG
printf("End of HTTP content\n");
diff --git a/ext/miniupnpc/minixml.c b/ext/miniupnpc/minixml.c
index 3e201ec2..935ec443 100644
--- a/ext/miniupnpc/minixml.c
+++ b/ext/miniupnpc/minixml.c
@@ -1,10 +1,11 @@
-/* $Id: minixml.c,v 1.11 2014/02/03 15:54:12 nanard Exp $ */
-/* minixml.c : the minimum size a xml parser can be ! */
+/* $Id: minixml.c,v 1.12 2017/12/12 11:17:40 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * minixml.c : the minimum size a xml parser can be ! */
/* Project : miniupnp
* webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* Author : Thomas Bernard
-Copyright (c) 2005-2014, Thomas BERNARD
+Copyright (c) 2005-2017, Thomas BERNARD
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -161,7 +162,8 @@ static void parseelt(struct xmlparser * p)
if (p->xml >= p->xmlend)
return;
}
- if(memcmp(p->xml, "<![CDATA[", 9) == 0)
+ /* CDATA are at least 9 + 3 characters long : <![CDATA[ ]]> */
+ if((p->xmlend >= (p->xml + (9 + 3))) && (memcmp(p->xml, "<![CDATA[", 9) == 0))
{
/* CDATA handling */
p->xml += 9;
diff --git a/ext/miniupnpc/pymoduletest.py b/ext/miniupnpc/pymoduletest.py
deleted file mode 100644
index 9fddd9c2..00000000
--- a/ext/miniupnpc/pymoduletest.py
+++ /dev/null
@@ -1,88 +0,0 @@
-#! /usr/bin/python
-# vim: tabstop=2 shiftwidth=2 expandtab
-# MiniUPnP project
-# Author : Thomas Bernard
-# This Sample code is public domain.
-# website : http://miniupnp.tuxfamily.org/
-
-# import the python miniupnpc module
-import miniupnpc
-import sys
-
-try:
- import argparse
- parser = argparse.ArgumentParser()
- parser.add_argument('-m', '--multicastif')
- parser.add_argument('-p', '--minissdpdsocket')
- parser.add_argument('-d', '--discoverdelay', type=int, default=200)
- parser.add_argument('-z', '--localport', type=int, default=0)
- # create the object
- u = miniupnpc.UPnP(**vars(parser.parse_args()))
-except:
- print 'argparse not available'
- i = 1
- multicastif = None
- minissdpdsocket = None
- discoverdelay = 200
- localport = 0
- while i < len(sys.argv):
- print sys.argv[i]
- if sys.argv[i] == '-m' or sys.argv[i] == '--multicastif':
- multicastif = sys.argv[i+1]
- elif sys.argv[i] == '-p' or sys.argv[i] == '--minissdpdsocket':
- minissdpdsocket = sys.argv[i+1]
- elif sys.argv[i] == '-d' or sys.argv[i] == '--discoverdelay':
- discoverdelay = int(sys.argv[i+1])
- elif sys.argv[i] == '-z' or sys.argv[i] == '--localport':
- localport = int(sys.argv[i+1])
- else:
- raise Exception('invalid argument %s' % sys.argv[i])
- i += 2
- # create the object
- u = miniupnpc.UPnP(multicastif, minissdpdsocket, discoverdelay, localport)
-
-print 'inital(default) values :'
-print ' discoverdelay', u.discoverdelay
-print ' lanaddr', u.lanaddr
-print ' multicastif', u.multicastif
-print ' minissdpdsocket', u.minissdpdsocket
-#u.minissdpdsocket = '../minissdpd/minissdpd.sock'
-# discovery process, it usualy takes several seconds (2 seconds or more)
-print 'Discovering... delay=%ums' % u.discoverdelay
-print u.discover(), 'device(s) detected'
-# select an igd
-try:
- u.selectigd()
-except Exception, e:
- print 'Exception :', e
- sys.exit(1)
-# display information about the IGD and the internet connection
-print 'local ip address :', u.lanaddr
-print 'external ip address :', u.externalipaddress()
-print u.statusinfo(), u.connectiontype()
-print 'total bytes : sent', u.totalbytesent(), 'received', u.totalbytereceived()
-print 'total packets : sent', u.totalpacketsent(), 'received', u.totalpacketreceived()
-
-#print u.addportmapping(64000, 'TCP',
-# '192.168.1.166', 63000, 'port mapping test', '')
-#print u.deleteportmapping(64000, 'TCP')
-
-port = 0
-proto = 'UDP'
-# list the redirections :
-i = 0
-while True:
- p = u.getgenericportmapping(i)
- if p==None:
- break
- print i, p
- (port, proto, (ihost,iport), desc, c, d, e) = p
- #print port, desc
- i = i + 1
-
-print u.getspecificportmapping(port, proto)
-try:
- print u.getportmappingnumberofentries()
-except Exception, e:
- print 'GetPortMappingNumberOfEntries() is not supported :', e
-
diff --git a/ext/miniupnpc/receivedata.c b/ext/miniupnpc/receivedata.c
index ef85a3db..5dbd227b 100644
--- a/ext/miniupnpc/receivedata.c
+++ b/ext/miniupnpc/receivedata.c
@@ -1,4 +1,4 @@
-/* $Id: receivedata.c,v 1.7 2015/11/09 21:51:41 nanard Exp $ */
+/* $Id: receivedata.c,v 1.8 2017/04/21 10:16:45 nanard Exp $ */
/* Project : miniupnp
* Website : http://miniupnp.free.fr/
* Author : Thomas Bernard
@@ -28,7 +28,7 @@
#endif /* _WIN32 */
#ifdef _WIN32
-#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#define PRINT_SOCKET_ERROR(x) fprintf(stderr, "Socket error: %s, %d\n", x, WSAGetLastError());
#else
#define PRINT_SOCKET_ERROR(x) perror(x)
#endif
diff --git a/ext/miniupnpc/testdesc/linksys_WAG200G_desc.values b/ext/miniupnpc/testdesc/linksys_WAG200G_desc.values
deleted file mode 100644
index cf422218..00000000
--- a/ext/miniupnpc/testdesc/linksys_WAG200G_desc.values
+++ /dev/null
@@ -1,14 +0,0 @@
-# values for linksys_WAG200G_desc.xml
-
-CIF:
- servicetype = urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1
- controlurl = /upnp/control/WANCommonIFC1
- eventsuburl = /upnp/event/WANCommonIFC1
- scpdurl = /cmnicfg.xml
-
-first:
- servicetype = urn:schemas-upnp-org:service:WANPPPConnection:1
- controlurl = /upnp/control/WANPPPConn1
- eventsuburl = /upnp/event/WANPPPConn1
- scpdurl = /pppcfg.xml
-
diff --git a/ext/miniupnpc/testdesc/linksys_WAG200G_desc.xml b/ext/miniupnpc/testdesc/linksys_WAG200G_desc.xml
deleted file mode 100644
index d428d73b..00000000
--- a/ext/miniupnpc/testdesc/linksys_WAG200G_desc.xml
+++ /dev/null
@@ -1,110 +0,0 @@
-<?xml version="1.0"?>
-<root xmlns="urn:schemas-upnp-org:device-1-0">
-<specVersion>
-<major>1</major>
-<minor>0</minor>
-</specVersion>
-<URLBase>http://192.168.1.1:49152</URLBase>
-<device>
-<deviceType>urn:schemas-upnp-org:device:InternetGatewayDevice:1</deviceType>
-<friendlyName>LINKSYS WAG200G Gateway</friendlyName>
-<manufacturer>LINKSYS</manufacturer>
-<manufacturerURL>http://www.linksys.com</manufacturerURL>
-<modelDescription>LINKSYS WAG200G Gateway</modelDescription>
-<modelName>Wireless-G ADSL Home Gateway</modelName>
-<modelNumber>WAG200G</modelNumber>
-<modelURL>http://www.linksys.com</modelURL>
-<serialNumber>123456789</serialNumber>
-<UDN>uuid:8ca2eb37-1dd2-11b2-86f1-001a709b5aa8</UDN>
-<UPC>WAG200G</UPC>
-<serviceList>
-<service>
-<serviceType>urn:schemas-upnp-org:service:Layer3Forwarding:1</serviceType>
-<serviceId>urn:upnp-org:serviceId:L3Forwarding1</serviceId>
-<controlURL>/upnp/control/L3Forwarding1</controlURL>
-<eventSubURL>/upnp/event/L3Forwarding1</eventSubURL>
-<SCPDURL>/l3frwd.xml</SCPDURL>
-</service>
-</serviceList>
-<deviceList>
-<device>
-<deviceType>urn:schemas-upnp-org:device:WANDevice:1</deviceType>
-<friendlyName>WANDevice</friendlyName>
-<manufacturer>LINKSYS</manufacturer>
-<manufacturerURL>http://www.linksys.com/</manufacturerURL>
-<modelDescription>Residential Gateway</modelDescription>
-<modelName>Internet Connection Sharing</modelName>
-<modelNumber>1</modelNumber>
-<modelURL>http://www.linksys.com/</modelURL>
-<serialNumber>0000001</serialNumber>
-<UDN>uuid:8ca2eb36-1dd2-11b2-86f1-001a709b5aa8</UDN>
-<UPC>WAG200G</UPC>
-<serviceList>
-<service>
-<serviceType>urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1</serviceType>
-<serviceId>urn:upnp-org:serviceId:WANCommonIFC1</serviceId>
-<controlURL>/upnp/control/WANCommonIFC1</controlURL>
-<eventSubURL>/upnp/event/WANCommonIFC1</eventSubURL>
-<SCPDURL>/cmnicfg.xml</SCPDURL>
-</service>
-</serviceList>
-<deviceList>
-<device>
-<deviceType>urn:schemas-upnp-org:device:WANConnectionDevice:1</deviceType>
-<friendlyName>WANConnectionDevice</friendlyName>
-<manufacturer>LINKSYS</manufacturer>
-<manufacturerURL>http://www.linksys.com/</manufacturerURL>
-<modelDescription>Residential Gateway</modelDescription>
-<modelName>Internet Connection Sharing</modelName>
-<modelNumber>1</modelNumber>
-<modelURL>http://www.linksys.com/</modelURL>
-<serialNumber>0000001</serialNumber>
-<UDN>uuid:8ca2eb37-1dd2-11b2-86f0-001a709b5aa8</UDN>
-<UPC>WAG200G</UPC>
-<serviceList>
-<service>
-<serviceType>urn:schemas-upnp-org:service:WANEthernetLinkConfig:1</serviceType>
-<serviceId>urn:upnp-org:serviceId:WANEthLinkC1</serviceId>
-<controlURL>/upnp/control/WANEthLinkC1</controlURL>
-<eventSubURL>/upnp/event/WANEthLinkC1</eventSubURL>
-<SCPDURL>/wanelcfg.xml</SCPDURL>
-</service>
-<service>
-<serviceType>urn:schemas-upnp-org:service:WANPPPConnection:1</serviceType>
-<serviceId>urn:upnp-org:serviceId:WANPPPConn1</serviceId>
-<controlURL>/upnp/control/WANPPPConn1</controlURL>
-<eventSubURL>/upnp/event/WANPPPConn1</eventSubURL>
-<SCPDURL>/pppcfg.xml</SCPDURL>
-</service>
-</serviceList>
-</device>
-</deviceList>
-</device>
-<device>
-<deviceType>urn:schemas-upnp-org:device:LANDevice:1</deviceType>
-<friendlyName>LANDevice</friendlyName>
-<manufacturer>LINKSYS</manufacturer>
-<manufacturerURL>http://www.linksys.com/</manufacturerURL>
-<modelDescription>Residential Gateway</modelDescription>
-<modelName>Residential Gateway</modelName>
-<modelNumber>1</modelNumber>
-<modelURL>http://www.linksys.com/</modelURL>
-<serialNumber>0000001</serialNumber>
-<UDN>uuid:8ca2eb36-1dd2-11b2-86f0-001a709b5aa
-8</UDN>
-<UPC>WAG200G</UPC>
-<serviceList>
-<service>
-<serviceType>urn:schemas-upnp-org:service:LANHostConfigManagement:1</serviceType>
-<serviceId>urn:upnp-org:serviceId:LANHostCfg1</serviceId>
-<controlURL>/upnp/control/LANHostCfg1</controlURL>
-<eventSubURL>/upnp/event/LANHostCfg1</eventSubURL>
-<SCPDURL>/lanhostc.xml</SCPDURL>
-</service>
-</serviceList>
-</device>
-</deviceList>
-<presentationURL>http://192.168.1.1/index.htm</presentationURL>
-</device>
-</root>
-
diff --git a/ext/miniupnpc/testdesc/new_LiveBox_desc.values b/ext/miniupnpc/testdesc/new_LiveBox_desc.values
deleted file mode 100644
index c55552e5..00000000
--- a/ext/miniupnpc/testdesc/new_LiveBox_desc.values
+++ /dev/null
@@ -1,20 +0,0 @@
-# values for new_LiveBox_desc.xml
-
-CIF:
- servicetype = urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1
- controlurl = /87895a19/upnp/control/WANCommonIFC1
- eventsuburl = /87895a19/upnp/control/WANCommonIFC1
- scpdurl = /87895a19/gateicfgSCPD.xml
-
-first:
- servicetype = urn:schemas-upnp-org:service:WANPPPConnection:2
- controlurl = /87895a19/upnp/control/WANIPConn1
- eventsuburl = /87895a19/upnp/control/WANIPConn1
- scpdurl = /87895a19/gateconnSCPD_PPP.xml
-
-IPv6FC:
- servicetype = urn:schemas-upnp-org:service:WANIPv6FirewallControl:1
- controlurl = /87895a19/upnp/control/WANIPv6FwCtrl1
- eventsuburl = /87895a19/upnp/control/WANIPv6FwCtrl1
- scpdurl = /87895a19/wanipv6fwctrlSCPD.xml
-
diff --git a/ext/miniupnpc/testdesc/new_LiveBox_desc.xml b/ext/miniupnpc/testdesc/new_LiveBox_desc.xml
deleted file mode 100644
index 620eb55a..00000000
--- a/ext/miniupnpc/testdesc/new_LiveBox_desc.xml
+++ /dev/null
@@ -1,90 +0,0 @@
-<?xml version="1.0"?>
-<root xmlns="urn:schemas-upnp-org:device-1-0">
- <specVersion>
- <major>1</major>
- <minor>0</minor>
- </specVersion>
- <device>
- <pnpx:X_hardwareId xmlns:pnpx="http://schemas.microsoft.com/windows/pnpx/2005/11">VEN_0129&amp;DEV_0000&amp;SUBSYS_03&amp;REV_250417</pnpx:X_hardwareId>
- <pnpx:X_compatibleId xmlns:pnpx="http://schemas.microsoft.com/windows/pnpx/2005/11">GenericUmPass</pnpx:X_compatibleId>
- <pnpx:X_deviceCategory xmlns:pnpx="http://schemas.microsoft.com/windows/pnpx/2005/11">NetworkInfrastructure.Gateway</pnpx:X_deviceCategory>
- <df:X_deviceCategory xmlns:df="http://schemas.microsoft.com/windows/2008/09/devicefoundation">Network.Gateway</df:X_deviceCategory>
- <deviceType>urn:schemas-upnp-org:device:InternetGatewayDevice:2</deviceType>
- <friendlyName>Orange Livebox</friendlyName>
- <manufacturer>Sagemcom</manufacturer>
- <manufacturerURL>http://www.sagemcom.com/</manufacturerURL>
- <modelName>Residential Livebox,(DSL,WAN Ethernet)</modelName>
- <UDN>uuid:87895a19-50f9-3736-a87f-115c230155f8</UDN>
- <modelDescription>Sagemcom,fr,SG30_sip-fr-4.28.35.1</modelDescription>
- <modelNumber>3</modelNumber>
- <serialNumber>LK14129DP441489</serialNumber>
- <presentationURL>http://192.168.1.1</presentationURL>
- <UPC></UPC>
- <iconList>
- <icon>
- <mimetype>image/png</mimetype>
- <width>16</width>
- <height>16</height>
- <depth>8</depth>
- <url>/87895a19/ligd.png</url>
- </icon>
- </iconList>
- <deviceList>
- <device>
- <deviceType>urn:schemas-upnp-org:device:WANDevice:2</deviceType>
- <friendlyName>WANDevice</friendlyName>
- <manufacturer>Sagemcom</manufacturer>
- <manufacturerURL>http://www.sagemcom.com/</manufacturerURL>
- <modelDescription>WAN Device on Sagemcom,fr,SG30_sip-fr-4.28.35.1</modelDescription>
- <modelName>Residential Livebox,(DSL,WAN Ethernet)</modelName>
- <modelNumber>3</modelNumber>
- <modelURL>http://www.sagemcom.com/</modelURL>
- <serialNumber>LK14129DP441489</serialNumber>
- <presentationURL>http://192.168.1.1</presentationURL>
- <UDN>uuid:e2397374-53d8-3fc6-8306-593ba1a34625</UDN>
- <UPC></UPC>
- <serviceList>
- <service>
- <serviceType>urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1</serviceType>
- <serviceId>urn:upnp-org:serviceId:WANCommonIFC1</serviceId>
- <controlURL>/87895a19/upnp/control/WANCommonIFC1</controlURL>
- <eventSubURL>/87895a19/upnp/control/WANCommonIFC1</eventSubURL>
- <SCPDURL>/87895a19/gateicfgSCPD.xml</SCPDURL>
- </service>
- </serviceList>
- <deviceList>
- <device>
- <deviceType>urn:schemas-upnp-org:device:WANConnectionDevice:2</deviceType>
- <friendlyName>WANConnectionDevice</friendlyName>
- <manufacturer>Sagemcom</manufacturer>
- <manufacturerURL>http://www.sagemcom.com/</manufacturerURL>
- <modelDescription>WanConnectionDevice on Sagemcom,fr,SG30_sip-fr-4.28.35.1</modelDescription>
- <modelName>Residential Livebox,(DSL,WAN Ethernet)</modelName>
- <modelNumber>3</modelNumber>
- <modelURL>http://www.sagemcom.com/</modelURL>
- <serialNumber>LK14129DP441489</serialNumber>
- <presentationURL>http://192.168.1.1</presentationURL>
- <UDN>uuid:44598a08-288e-32c9-8a4d-d3c008ede331</UDN>
- <UPC></UPC>
- <serviceList>
- <service>
- <serviceType>urn:schemas-upnp-org:service:WANPPPConnection:2</serviceType>
- <serviceId>urn:upnp-org:serviceId:WANIPConn1</serviceId>
- <controlURL>/87895a19/upnp/control/WANIPConn1</controlURL>
- <eventSubURL>/87895a19/upnp/control/WANIPConn1</eventSubURL>
- <SCPDURL>/87895a19/gateconnSCPD_PPP.xml</SCPDURL>
- </service>
- <service>
- <serviceType>urn:schemas-upnp-org:service:WANIPv6FirewallControl:1</serviceType>
- <serviceId>urn:upnp-org:serviceId:WANIPv6FwCtrl1</serviceId>
- <controlURL>/87895a19/upnp/control/WANIPv6FwCtrl1</controlURL>
- <eventSubURL>/87895a19/upnp/control/WANIPv6FwCtrl1</eventSubURL>
- <SCPDURL>/87895a19/wanipv6fwctrlSCPD.xml</SCPDURL>
- </service>
- </serviceList>
- </device>
- </deviceList>
- </device>
- </deviceList>
- </device>
-</root> \ No newline at end of file
diff --git a/ext/miniupnpc/testigddescparse.c b/ext/miniupnpc/testigddescparse.c
deleted file mode 100644
index c1907fd0..00000000
--- a/ext/miniupnpc/testigddescparse.c
+++ /dev/null
@@ -1,187 +0,0 @@
-/* $Id: testigddescparse.c,v 1.10 2015/08/06 09:55:24 nanard Exp $ */
-/* Project : miniupnp
- * http://miniupnp.free.fr/
- * Author : Thomas Bernard
- * Copyright (c) 2008-2015 Thomas Bernard
- * This software is subject to the conditions detailed in the
- * LICENCE file provided in this distribution.
- * */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "igd_desc_parse.h"
-#include "minixml.h"
-#include "miniupnpc.h"
-
-/* count number of differences */
-int compare_service(struct IGDdatas_service * s, FILE * f)
-{
- int n = 0;
- char line[1024];
-
- while(fgets(line, sizeof(line), f)) {
- char * value;
- char * equal;
- char * name;
- char * parsedvalue;
- int l;
- l = strlen(line);
- while((l > 0) && ((line[l-1] == '\r') || (line[l-1] == '\n') || (line[l-1] == ' ')))
- line[--l] = '\0';
- if(l == 0)
- break; /* end on blank line */
- if(line[0] == '#')
- continue; /* skip comments */
- equal = strchr(line, '=');
- if(equal == NULL) {
- fprintf(stderr, "Warning, line does not contain '=' : %s\n", line);
- continue;
- }
- *equal = '\0';
- name = line;
- while(*name == ' ' || *name == '\t')
- name++;
- l = strlen(name);
- while((l > 0) && (name[l-1] == ' ' || name[l-1] == '\t'))
- name[--l] = '\0';
- value = equal + 1;
- while(*value == ' ' || *value == '\t')
- value++;
- if(strcmp(name, "controlurl") == 0)
- parsedvalue = s->controlurl;
- else if(strcmp(name, "eventsuburl") == 0)
- parsedvalue = s->eventsuburl;
- else if(strcmp(name, "scpdurl") == 0)
- parsedvalue = s->scpdurl;
- else if(strcmp(name, "servicetype") == 0)
- parsedvalue = s->servicetype;
- else {
- fprintf(stderr, "unknown field '%s'\n", name);
- continue;
- }
- if(0 != strcmp(parsedvalue, value)) {
- fprintf(stderr, "difference : '%s' != '%s'\n", parsedvalue, value);
- n++;
- }
- }
- return n;
-}
-
-int compare_igd(struct IGDdatas * p, FILE * f)
-{
- int n = 0;
- char line[1024];
- struct IGDdatas_service * s;
-
- while(fgets(line, sizeof(line), f)) {
- char * colon;
- int l = (int)strlen(line);
- while((l > 0) && (line[l-1] == '\r' || (line[l-1] == '\n')))
- line[--l] = '\0';
- if(l == 0 || line[0] == '#')
- continue; /* skip blank lines and comments */
- colon = strchr(line, ':');
- if(colon == NULL) {
- fprintf(stderr, "Warning, no ':' : %s\n", line);
- continue;
- }
- s = NULL;
- *colon = '\0';
- if(strcmp(line, "CIF") == 0)
- s = &p->CIF;
- else if(strcmp(line, "first") == 0)
- s = &p->first;
- else if(strcmp(line, "second") == 0)
- s = &p->second;
- else if(strcmp(line, "IPv6FC") == 0)
- s = &p->IPv6FC;
- else {
- s = NULL;
- fprintf(stderr, "*** unknown service '%s' ***\n", line);
- n++;
- continue;
- }
- n += compare_service(s, f);
- }
- if(n > 0)
- fprintf(stderr, "*** %d difference%s ***\n", n, (n > 1) ? "s" : "");
- return n;
-}
-
-int test_igd_desc_parse(char * buffer, int len, FILE * f)
-{
- int n;
- struct IGDdatas igd;
- struct xmlparser parser;
- struct UPNPUrls urls;
-
- memset(&igd, 0, sizeof(struct IGDdatas));
- memset(&parser, 0, sizeof(struct xmlparser));
- parser.xmlstart = buffer;
- parser.xmlsize = len;
- parser.data = &igd;
- parser.starteltfunc = IGDstartelt;
- parser.endeltfunc = IGDendelt;
- parser.datafunc = IGDdata;
- parsexml(&parser);
-#ifdef DEBUG
- printIGD(&igd);
-#endif /* DEBUG */
- GetUPNPUrls(&urls, &igd, "http://fake/desc/url/file.xml", 0);
- printf("ipcondescURL='%s'\n", urls.ipcondescURL);
- printf("controlURL='%s'\n", urls.controlURL);
- printf("controlURL_CIF='%s'\n", urls.controlURL_CIF);
- n = f ? compare_igd(&igd, f) : 0;
- FreeUPNPUrls(&urls);
- return n;
-}
-
-int main(int argc, char * * argv)
-{
- FILE * f;
- char * buffer;
- int len;
- int r;
- if(argc<2) {
- fprintf(stderr, "Usage: %s file.xml [file.values]\n", argv[0]);
- return 1;
- }
- f = fopen(argv[1], "r");
- if(!f) {
- fprintf(stderr, "Cannot open %s for reading.\n", argv[1]);
- return 1;
- }
- fseek(f, 0, SEEK_END);
- len = ftell(f);
- fseek(f, 0, SEEK_SET);
- buffer = malloc(len);
- if(!buffer) {
- fprintf(stderr, "Memory allocation error.\n");
- fclose(f);
- return 1;
- }
- r = (int)fread(buffer, 1, len, f);
- if(r != len) {
- fprintf(stderr, "Failed to read file %s. %d out of %d bytes.\n",
- argv[1], r, len);
- fclose(f);
- free(buffer);
- return 1;
- }
- fclose(f);
- f = NULL;
- if(argc > 2) {
- f = fopen(argv[2], "r");
- if(!f) {
- fprintf(stderr, "Cannot open %s for reading.\n", argv[2]);
- free(buffer);
- return 1;
- }
- }
- r = test_igd_desc_parse(buffer, len, f);
- free(buffer);
- if(f)
- fclose(f);
- return r;
-}
-
diff --git a/ext/miniupnpc/testminiwget.c b/ext/miniupnpc/testminiwget.c
deleted file mode 100644
index 5eb49ec1..00000000
--- a/ext/miniupnpc/testminiwget.c
+++ /dev/null
@@ -1,55 +0,0 @@
-/* $Id: testminiwget.c,v 1.5 2016/01/24 17:24:36 nanard Exp $ */
-/* Project : miniupnp
- * Author : Thomas Bernard
- * Copyright (c) 2005-2016 Thomas Bernard
- * This software is subject to the conditions detailed in the
- * LICENCE file provided in this distribution.
- * */
-#include <stdio.h>
-#include <stdlib.h>
-#include "miniwget.h"
-
-/**
- * This program uses the miniwget / miniwget_getaddr function
- * from miniwget.c in order to retreive a web ressource using
- * a GET HTTP method, and store it in a file.
- */
-int main(int argc, char * * argv)
-{
- void * data;
- int size, writtensize;
- FILE *f;
- char addr[64];
- int status_code = -1;
-
- if(argc < 3) {
- fprintf(stderr, "Usage:\t%s url file\n", argv[0]);
- fprintf(stderr, "Example:\t%s http://www.google.com/ out.html\n", argv[0]);
- return 1;
- }
- data = miniwget_getaddr(argv[1], &size, addr, sizeof(addr), 0, &status_code);
- if(!data || (status_code != 200)) {
- if(data) free(data);
- fprintf(stderr, "Error %d fetching %s\n", status_code, argv[1]);
- return 1;
- }
- printf("local address : %s\n", addr);
- printf("got %d bytes\n", size);
- f = fopen(argv[2], "wb");
- if(!f) {
- fprintf(stderr, "Cannot open file %s for writing\n", argv[2]);
- free(data);
- return 1;
- }
- writtensize = fwrite(data, 1, size, f);
- if(writtensize != size) {
- fprintf(stderr, "Could only write %d bytes out of %d to %s\n",
- writtensize, size, argv[2]);
- } else {
- printf("%d bytes written to %s\n", writtensize, argv[2]);
- }
- fclose(f);
- free(data);
- return 0;
-}
-
diff --git a/ext/miniupnpc/testminiwget.sh b/ext/miniupnpc/testminiwget.sh
deleted file mode 100755
index 690b4056..00000000
--- a/ext/miniupnpc/testminiwget.sh
+++ /dev/null
@@ -1,96 +0,0 @@
-#!/bin/sh
-# $Id: testminiwget.sh,v 1.13 2015/09/03 17:57:44 nanard Exp $
-# project miniupnp : http://miniupnp.free.fr/
-# (c) 2011-2015 Thomas Bernard
-#
-# test program for miniwget.c
-# is usually invoked by "make check"
-#
-# This test program :
-# 1 - launches a local HTTP server (minihttptestserver)
-# 2 - uses testminiwget to retreive data from this server
-# 3 - compares served and received data
-# 4 - kills the local HTTP server and exits
-#
-# The script was tested and works with ksh, bash
-# it should now also run with dash
-
-TMPD=`mktemp -d -t miniwgetXXXXXXXXXX`
-HTTPSERVEROUT="${TMPD}/httpserverout"
-EXPECTEDFILE="${TMPD}/expectedfile"
-DOWNLOADEDFILE="${TMPD}/downloadedfile"
-PORT=
-RET=0
-
-case "$HAVE_IPV6" in
- n|no|0)
- ADDR=localhost
- SERVERARGS=""
- ;;
- *)
- ADDR="[::1]"
- SERVERARGS="-6"
- ;;
-
-esac
-
-#make minihttptestserver
-#make testminiwget
-
-# launching the test HTTP server
-./minihttptestserver $SERVERARGS -e $EXPECTEDFILE > $HTTPSERVEROUT &
-SERVERPID=$!
-while [ -z "$PORT" ]; do
- sleep 1
- PORT=`cat $HTTPSERVEROUT | sed 's/Listening on port \([0-9]*\)/\1/' `
-done
-echo "Test HTTP server is listening on $PORT"
-
-URL1="http://$ADDR:$PORT/index.html"
-URL2="http://$ADDR:$PORT/chunked"
-URL3="http://$ADDR:$PORT/addcrap"
-
-echo "standard test ..."
-./testminiwget $URL1 "${DOWNLOADEDFILE}.1"
-if cmp $EXPECTEDFILE "${DOWNLOADEDFILE}.1" ; then
- echo "ok"
-else
- echo "standard test FAILED"
- RET=1
-fi
-
-echo "chunked transfert encoding test ..."
-./testminiwget $URL2 "${DOWNLOADEDFILE}.2"
-if cmp $EXPECTEDFILE "${DOWNLOADEDFILE}.2" ; then
- echo "ok"
-else
- echo "chunked transfert encoding test FAILED"
- RET=1
-fi
-
-echo "response too long test ..."
-./testminiwget $URL3 "${DOWNLOADEDFILE}.3"
-if cmp $EXPECTEDFILE "${DOWNLOADEDFILE}.3" ; then
- echo "ok"
-else
- echo "response too long test FAILED"
- RET=1
-fi
-
-# kill the test HTTP server
-kill $SERVERPID
-wait $SERVERPID
-
-# remove temporary files (for success cases)
-if [ $RET -eq 0 ]; then
- rm -f "${DOWNLOADEDFILE}.1"
- rm -f "${DOWNLOADEDFILE}.2"
- rm -f "${DOWNLOADEDFILE}.3"
- rm -f $EXPECTEDFILE $HTTPSERVEROUT
- rmdir ${TMPD}
-else
- echo "at least one of the test FAILED"
- echo "directory ${TMPD} is left intact"
-fi
-exit $RET
-
diff --git a/ext/miniupnpc/testminixml.c b/ext/miniupnpc/testminixml.c
deleted file mode 100644
index 57c4a85e..00000000
--- a/ext/miniupnpc/testminixml.c
+++ /dev/null
@@ -1,89 +0,0 @@
-/* $Id: testminixml.c,v 1.10 2014/11/17 17:19:13 nanard Exp $
- * MiniUPnP project
- * Website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- * Author : Thomas Bernard.
- * Copyright (c) 2005-2014 Thomas Bernard
- *
- * testminixml.c
- * test program for the "minixml" functions.
- */
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include "minixml.h"
-#include "igd_desc_parse.h"
-
-/* ---------------------------------------------------------------------- */
-void printeltname1(void * d, const char * name, int l)
-{
- int i;
- (void)d;
- printf("element ");
- for(i=0;i<l;i++)
- putchar(name[i]);
-}
-void printeltname2(void * d, const char * name, int l)
-{
- int i;
- (void)d;
- putchar('/');
- for(i=0;i<l;i++)
- putchar(name[i]);
- putchar('\n');
-}
-void printdata(void *d, const char * data, int l)
-{
- int i;
- (void)d;
- printf("data : ");
- for(i=0;i<l;i++)
- putchar(data[i]);
- putchar('\n');
-}
-
-void burptest(const char * buffer, int bufsize)
-{
- struct IGDdatas data;
- struct xmlparser parser;
- /*objet IGDdatas */
- memset(&data, 0, sizeof(struct IGDdatas));
- /* objet xmlparser */
- parser.xmlstart = buffer;
- parser.xmlsize = bufsize;
- parser.data = &data;
- /*parser.starteltfunc = printeltname1;
- parser.endeltfunc = printeltname2;
- parser.datafunc = printdata; */
- parser.starteltfunc = IGDstartelt;
- parser.endeltfunc = IGDendelt;
- parser.datafunc = IGDdata;
- parsexml(&parser);
-#ifdef DEBUG
- printIGD(&data);
-#endif /* DEBUG */
-}
-
-/* ----- main ---- */
-#define XML_MAX_SIZE (8192)
-int main(int argc, char * * argv)
-{
- FILE * f;
- char buffer[XML_MAX_SIZE];
- int bufsize;
- if(argc<2)
- {
- printf("usage:\t%s file.xml\n", argv[0]);
- return 1;
- }
- f = fopen(argv[1], "r");
- if(!f)
- {
- printf("cannot open file %s\n", argv[1]);
- return 1;
- }
- bufsize = (int)fread(buffer, 1, XML_MAX_SIZE, f);
- fclose(f);
- burptest(buffer, bufsize);
- return 0;
-}
-
diff --git a/ext/miniupnpc/testportlistingparse.c b/ext/miniupnpc/testportlistingparse.c
deleted file mode 100644
index bd9247dc..00000000
--- a/ext/miniupnpc/testportlistingparse.c
+++ /dev/null
@@ -1,151 +0,0 @@
-/* $Id: testportlistingparse.c,v 1.2 2014/11/01 10:37:32 nanard Exp $ */
-/* Project : miniupnp
- * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- * Author : Thomas Bernard
- * Copyright (c) 2014 Thomas Bernard
- * This software is subject to the conditions detailed in the
- * LICENCE file provided in this distribution.
- * */
-
-#include <string.h>
-#include <stdio.h>
-#include "portlistingparse.h"
-
-struct port_mapping {
- unsigned int leasetime;
- unsigned short externalport;
- unsigned short internalport;
- const char * remotehost;
- const char * client;
- const char * proto;
- const char * desc;
- unsigned char enabled;
-};
-
-/* return the number of differences */
-int test(const char * portListingXml, int portListingXmlLen,
- const struct port_mapping * ref, int count)
-{
- int i;
- int r = 0;
- struct PortMappingParserData data;
- struct PortMapping * pm;
-
- memset(&data, 0, sizeof(data));
- ParsePortListing(portListingXml, portListingXmlLen, &data);
- for(i = 0, pm = data.l_head;
- (pm != NULL) && (i < count);
- i++, pm = pm->l_next) {
- printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n",
- i, pm->protocol, pm->externalPort, pm->internalClient,
- pm->internalPort,
- pm->description, pm->remoteHost,
- (unsigned)pm->leaseTime);
- if(0 != strcmp(pm->protocol, ref[i].proto)) {
- printf("protocol : '%s' != '%s'\n", pm->protocol, ref[i].proto);
- r++;
- }
- if(pm->externalPort != ref[i].externalport) {
- printf("externalPort : %hu != %hu\n",
- pm->externalPort, ref[i].externalport);
- r++;
- }
- if(0 != strcmp(pm->internalClient, ref[i].client)) {
- printf("client : '%s' != '%s'\n",
- pm->internalClient, ref[i].client);
- r++;
- }
- if(pm->internalPort != ref[i].internalport) {
- printf("internalPort : %hu != %hu\n",
- pm->internalPort, ref[i].internalport);
- r++;
- }
- if(0 != strcmp(pm->description, ref[i].desc)) {
- printf("description : '%s' != '%s'\n",
- pm->description, ref[i].desc);
- r++;
- }
- if(0 != strcmp(pm->remoteHost, ref[i].remotehost)) {
- printf("remoteHost : '%s' != '%s'\n",
- pm->remoteHost, ref[i].remotehost);
- r++;
- }
- if((unsigned)pm->leaseTime != ref[i].leasetime) {
- printf("leaseTime : %u != %u\n",
- (unsigned)pm->leaseTime, ref[i].leasetime);
- r++;
- }
- if(pm->enabled != ref[i].enabled) {
- printf("enabled : %d != %d\n",
- (int)pm->enabled, (int)ref[i].enabled);
- r++;
- }
- }
- if((i != count) || (pm != NULL)) {
- printf("count mismatch : i=%d count=%d pm=%p\n", i, count, pm);
- r++;
- }
- FreePortListing(&data);
- return r;
-}
-
-const char test_document[] =
-"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-"<p:PortMappingList xmlns:p=\"urn:schemas-upnp-org:gw:WANIPConnection\"\n"
-"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \n"
-"xsi:schemaLocation=\"urn:schemas-upnp-org:gw:WANIPConnection "
-"http://www.upnp.org/schemas/gw/WANIPConnection-v2.xsd\">\n"
-" <p:PortMappingEntry>\n"
-" <p:NewRemoteHost></p:NewRemoteHost>\n"
-" <p:NewExternalPort>5002</p:NewExternalPort>\n"
-" <p:NewProtocol>UDP</p:NewProtocol>\n"
-" <p:NewInternalPort>4001</p:NewInternalPort>\n"
-" <p:NewInternalClient>192.168.1.123</p:NewInternalClient>\n"
-" <p:NewEnabled>1</p:NewEnabled>\n"
-" <p:NewDescription>xxx</p:NewDescription>\n"
-" <p:NewLeaseTime>0</p:NewLeaseTime>\n"
-" </p:PortMappingEntry>\n"
-" <p:PortMappingEntry>\n"
-" <p:NewRemoteHost>202.233.2.1</p:NewRemoteHost>\n"
-" <p:NewExternalPort>2345</p:NewExternalPort>\n"
-" <p:NewProtocol>TCP</p:NewProtocol>\n"
-" <p:NewInternalPort>2349</p:NewInternalPort>\n"
-" <p:NewInternalClient>192.168.1.137</p:NewInternalClient>\n"
-" <p:NewEnabled>1</p:NewEnabled>\n"
-" <p:NewDescription>dooom</p:NewDescription>\n"
-" <p:NewLeaseTime>346</p:NewLeaseTime>\n"
-" </p:PortMappingEntry>\n"
-" <p:PortMappingEntry>\n"
-" <p:NewRemoteHost>134.231.2.11</p:NewRemoteHost>\n"
-" <p:NewExternalPort>12345</p:NewExternalPort>\n"
-" <p:NewProtocol>TCP</p:NewProtocol>\n"
-" <p:NewInternalPort>12345</p:NewInternalPort>\n"
-" <p:NewInternalClient>192.168.1.137</p:NewInternalClient>\n"
-" <p:NewEnabled>1</p:NewEnabled>\n"
-" <p:NewDescription>dooom A</p:NewDescription>\n"
-" <p:NewLeaseTime>347</p:NewLeaseTime>\n"
-" </p:PortMappingEntry>\n"
-"</p:PortMappingList>";
-
-#define PORT_MAPPINGS_COUNT 3
-const struct port_mapping port_mappings[PORT_MAPPINGS_COUNT] = {
-{347, 12345, 12345, "134.231.2.11", "192.168.1.137", "TCP", "dooom A", 1},
-{346, 2345, 2349, "202.233.2.1", "192.168.1.137", "TCP", "dooom", 1},
-{0, 5002, 4001, "", "192.168.1.123", "UDP", "xxx", 1}
-};
-
-/* --- main --- */
-int main(void)
-{
- int r;
- r = test(test_document, sizeof(test_document) - 1,
- port_mappings, PORT_MAPPINGS_COUNT);
- if(r == 0) {
- printf("test of portlistingparse OK\n");
- return 0;
- } else {
- printf("test FAILED (%d differences counted)\n", r);
- return 1;
- }
-}
-
diff --git a/ext/miniupnpc/testreplyparse/DeletePortMapping.namevalue b/ext/miniupnpc/testreplyparse/DeletePortMapping.namevalue
deleted file mode 100644
index 48ca0ccc..00000000
--- a/ext/miniupnpc/testreplyparse/DeletePortMapping.namevalue
+++ /dev/null
@@ -1,3 +0,0 @@
-NewRemoteHost=
-NewExternalPort=123
-NewProtocol=TCP
diff --git a/ext/miniupnpc/testreplyparse/DeletePortMapping.xml b/ext/miniupnpc/testreplyparse/DeletePortMapping.xml
deleted file mode 100644
index a955c53f..00000000
--- a/ext/miniupnpc/testreplyparse/DeletePortMapping.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0"?>
-<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:DeletePortMapping xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"><NewRemoteHost></NewRemoteHost><NewExternalPort>123</NewExternalPort>
-<NewProtocol>TCP</NewProtocol></u:DeletePortMapping></s:Body>
-
-</s:Envelope>
-
diff --git a/ext/miniupnpc/testreplyparse/GetExternalIPAddress.namevalue b/ext/miniupnpc/testreplyparse/GetExternalIPAddress.namevalue
deleted file mode 100644
index 5aa75f88..00000000
--- a/ext/miniupnpc/testreplyparse/GetExternalIPAddress.namevalue
+++ /dev/null
@@ -1,2 +0,0 @@
-NewExternalIPAddress=1.2.3.4
-
diff --git a/ext/miniupnpc/testreplyparse/GetExternalIPAddress.xml b/ext/miniupnpc/testreplyparse/GetExternalIPAddress.xml
deleted file mode 100644
index db7ec1f9..00000000
--- a/ext/miniupnpc/testreplyparse/GetExternalIPAddress.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-<?xml version="1.0"?><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:GetExternalIPAddressResponse xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"><NewExternalIPAddress>1.2.3.4</NewExternalIPAddress></u:GetExternalIPAddressResponse></s:Body></s:Envelope>
-
diff --git a/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.namevalue b/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.namevalue
deleted file mode 100644
index 26b169c3..00000000
--- a/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.namevalue
+++ /dev/null
@@ -1,3 +0,0 @@
-NewProtocol=UDP
-NewExternalPort=12345
-NewRemoteHost=
diff --git a/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.xml b/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.xml
deleted file mode 100644
index bbb540ea..00000000
--- a/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<?xml version="1.0"?>
-<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:GetSpecificPortMappingEntry xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"><NewRemoteHost></NewRemoteHost><NewExternalPort>12345</NewExternalPort><NewProtocol>UDP</NewProtocol></u:GetSpecificPortMappingEntry></s:Body></s:Envelope>
-
diff --git a/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.namevalue b/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.namevalue
deleted file mode 100644
index 2189789b..00000000
--- a/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.namevalue
+++ /dev/null
@@ -1,5 +0,0 @@
-NewInternalPort=12345
-NewInternalClient=192.168.10.110
-NewEnabled=1
-NewPortMappingDescription=libminiupnpc
-NewLeaseDuration=0
diff --git a/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.xml b/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.xml
deleted file mode 100644
index 77e8d9c7..00000000
--- a/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-<?xml version="1.0"?><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:GetSpecificPortMappingEntryResponse xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"><NewInternalPort>12345</NewInternalPort><NewInternalClient>192.168.10.110</NewInternalClient><NewEnabled>1</NewEnabled><NewPortMappingDescription>libminiupnpc</NewPortMappingDescription><NewLeaseDuration>0</NewLeaseDuration></u:GetSpecificPortMappingEntryResponse></s:Body></s:Envelope>
-
diff --git a/ext/miniupnpc/testreplyparse/SetDefaultConnectionService.namevalue b/ext/miniupnpc/testreplyparse/SetDefaultConnectionService.namevalue
deleted file mode 100644
index f78c7e2a..00000000
--- a/ext/miniupnpc/testreplyparse/SetDefaultConnectionService.namevalue
+++ /dev/null
@@ -1 +0,0 @@
-NewDefaultConnectionService=uuid:c6c05a33-f704-48df-9910-e099b3471d81:WANConnectionDevice:1,INVALID_SERVICE_ID
diff --git a/ext/miniupnpc/testreplyparse/SetDefaultConnectionService.xml b/ext/miniupnpc/testreplyparse/SetDefaultConnectionService.xml
deleted file mode 100644
index ac04c07a..00000000
--- a/ext/miniupnpc/testreplyparse/SetDefaultConnectionService.xml
+++ /dev/null
@@ -1 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><u:SetDefaultConnectionService xmlns:u="urn:schemas-upnp-org:service:Layer3Forwarding:1"><NewDefaultConnectionService>uuid:c6c05a33-f704-48df-9910-e099b3471d81:WANConnectionDevice:1,INVALID_SERVICE_ID</NewDefaultConnectionService></u:SetDefaultConnectionService></s:Body></s:Envelope>
diff --git a/ext/miniupnpc/testreplyparse/readme.txt b/ext/miniupnpc/testreplyparse/readme.txt
deleted file mode 100644
index 3eb1f015..00000000
--- a/ext/miniupnpc/testreplyparse/readme.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This directory contains files used for validation of upnpreplyparse.c code.
-
-Each .xml file to parse should give the results which are in the .namevalue
-file.
-
-A .namevalue file contain name=value lines.
-
diff --git a/ext/miniupnpc/testupnpigd.py b/ext/miniupnpc/testupnpigd.py
deleted file mode 100755
index 6d167a4c..00000000
--- a/ext/miniupnpc/testupnpigd.py
+++ /dev/null
@@ -1,84 +0,0 @@
-#! /usr/bin/python
-# $Id: testupnpigd.py,v 1.4 2008/10/11 10:27:20 nanard Exp $
-# MiniUPnP project
-# Author : Thomas Bernard
-# This Sample code is public domain.
-# website : http://miniupnp.tuxfamily.org/
-
-# import the python miniupnpc module
-import miniupnpc
-import socket
-import BaseHTTPServer
-
-# function definition
-def list_redirections():
- i = 0
- while True:
- p = u.getgenericportmapping(i)
- if p==None:
- break
- print i, p
- i = i + 1
-
-#define the handler class for HTTP connections
-class handler_class(BaseHTTPServer.BaseHTTPRequestHandler):
- def do_GET(self):
- self.send_response(200)
- self.end_headers()
- self.wfile.write("OK MON GARS")
-
-# create the object
-u = miniupnpc.UPnP()
-#print 'inital(default) values :'
-#print ' discoverdelay', u.discoverdelay
-#print ' lanaddr', u.lanaddr
-#print ' multicastif', u.multicastif
-#print ' minissdpdsocket', u.minissdpdsocket
-u.discoverdelay = 200;
-
-try:
- print 'Discovering... delay=%ums' % u.discoverdelay
- ndevices = u.discover()
- print ndevices, 'device(s) detected'
-
- # select an igd
- u.selectigd()
- # display information about the IGD and the internet connection
- print 'local ip address :', u.lanaddr
- externalipaddress = u.externalipaddress()
- print 'external ip address :', externalipaddress
- print u.statusinfo(), u.connectiontype()
-
- #instanciate a HTTPd object. The port is assigned by the system.
- httpd = BaseHTTPServer.HTTPServer((u.lanaddr, 0), handler_class)
- eport = httpd.server_port
-
- # find a free port for the redirection
- r = u.getspecificportmapping(eport, 'TCP')
- while r != None and eport < 65536:
- eport = eport + 1
- r = u.getspecificportmapping(eport, 'TCP')
-
- print 'trying to redirect %s port %u TCP => %s port %u TCP' % (externalipaddress, eport, u.lanaddr, httpd.server_port)
-
- b = u.addportmapping(eport, 'TCP', u.lanaddr, httpd.server_port,
- 'UPnP IGD Tester port %u' % eport, '')
- if b:
- print 'Success. Now waiting for some HTTP request on http://%s:%u' % (externalipaddress ,eport)
- try:
- httpd.handle_request()
- httpd.server_close()
- except KeyboardInterrupt, details:
- print "CTRL-C exception!", details
- b = u.deleteportmapping(eport, 'TCP')
- if b:
- print 'Successfully deleted port mapping'
- else:
- print 'Failed to remove port mapping'
- else:
- print 'Failed'
-
- httpd.server_close()
-
-except Exception, e:
- print 'Exception :', e
diff --git a/ext/miniupnpc/testupnpreplyparse.c b/ext/miniupnpc/testupnpreplyparse.c
deleted file mode 100644
index 7ba7131e..00000000
--- a/ext/miniupnpc/testupnpreplyparse.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/* $Id: testupnpreplyparse.c,v 1.4 2014/01/27 11:45:19 nanard Exp $ */
-/* MiniUPnP project
- * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- * (c) 2006-2014 Thomas Bernard
- * This software is subject to the conditions detailed
- * in the LICENCE file provided within the distribution */
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include "upnpreplyparse.h"
-
-int
-test_parsing(const char * buf, int len, FILE * f)
-{
- char line[1024];
- struct NameValueParserData pdata;
- int ok = 1;
- ParseNameValue(buf, len, &pdata);
- /* check result */
- if(f != NULL)
- {
- while(fgets(line, sizeof(line), f))
- {
- char * value;
- char * equal;
- char * parsedvalue;
- int l;
- l = strlen(line);
- while((l > 0) && ((line[l-1] == '\r') || (line[l-1] == '\n')))
- line[--l] = '\0';
- /* skip empty lines */
- if(l == 0)
- continue;
- equal = strchr(line, '=');
- if(equal == NULL)
- {
- fprintf(stderr, "Warning, line does not contain '=' : %s\n", line);
- continue;
- }
- *equal = '\0';
- value = equal + 1;
- parsedvalue = GetValueFromNameValueList(&pdata, line);
- if((parsedvalue == NULL) || (strcmp(parsedvalue, value) != 0))
- {
- fprintf(stderr, "Element <%s> : expecting value '%s', got '%s'\n",
- line, value, parsedvalue ? parsedvalue : "<null string>");
- ok = 0;
- }
- }
- }
- ClearNameValueList(&pdata);
- return ok;
-}
-
-int main(int argc, char * * argv)
-{
- FILE * f;
- char buffer[4096];
- int l;
- int ok;
-
- if(argc<2)
- {
- fprintf(stderr, "Usage: %s file.xml [file.namevalues]\n", argv[0]);
- return 1;
- }
- f = fopen(argv[1], "r");
- if(!f)
- {
- fprintf(stderr, "Error : can not open file %s\n", argv[1]);
- return 2;
- }
- l = fread(buffer, 1, sizeof(buffer)-1, f);
- fclose(f);
- f = NULL;
- buffer[l] = '\0';
- if(argc > 2)
- {
- f = fopen(argv[2], "r");
- if(!f)
- {
- fprintf(stderr, "Error : can not open file %s\n", argv[2]);
- return 2;
- }
- }
-#ifdef DEBUG
- DisplayNameValueList(buffer, l);
-#endif
- ok = test_parsing(buffer, l, f);
- if(f)
- {
- fclose(f);
- }
- return ok ? 0 : 3;
-}
-
diff --git a/ext/miniupnpc/testupnpreplyparse.sh b/ext/miniupnpc/testupnpreplyparse.sh
deleted file mode 100755
index 992930b7..00000000
--- a/ext/miniupnpc/testupnpreplyparse.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/sh
-
-for f in testreplyparse/*.xml ; do
- bf="`dirname $f`/`basename $f .xml`"
- if ./testupnpreplyparse $f $bf.namevalue ; then
- echo "$f : passed"
- else
- echo "$f : FAILED"
- exit 1
- fi
-done
-
-exit 0
-
diff --git a/ext/miniupnpc/upnpc.c b/ext/miniupnpc/upnpc.c
index 8e7edadd..e719ecec 100644
--- a/ext/miniupnpc/upnpc.c
+++ b/ext/miniupnpc/upnpc.c
@@ -1,7 +1,7 @@
-/* $Id: upnpc.c,v 1.115 2016/10/07 09:04:01 nanard Exp $ */
+/* $Id: upnpc.c,v 1.117 2017/05/26 15:26:55 nanard Exp $ */
/* Project : miniupnp
* Author : Thomas Bernard
- * Copyright (c) 2005-2016 Thomas Bernard
+ * Copyright (c) 2005-2017 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
@@ -292,9 +292,11 @@ static int SetRedirectAndTest(struct UPNPUrls * urls,
r = UPNP_AddPortMapping(urls->controlURL, data->first.servicetype,
eport, iport, iaddr, description,
proto, 0, leaseDuration);
- if(r!=UPNPCOMMAND_SUCCESS)
+ if(r!=UPNPCOMMAND_SUCCESS) {
printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n",
eport, iport, iaddr, r, strupnperror(r));
+ return -2;
+ }
}
r = UPNP_GetSpecificPortMappingEntry(urls->controlURL,
@@ -576,8 +578,8 @@ int main(int argc, char ** argv)
}
#endif
printf("upnpc : miniupnpc library test client, version %s.\n", MINIUPNPC_VERSION_STRING);
- printf(" (c) 2005-2016 Thomas Bernard.\n");
- printf("Go to http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/\n"
+ printf(" (c) 2005-2017 Thomas Bernard.\n");
+ printf("Go to http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/\n"
"for more information.\n");
/* command line processing */
for(i=1; i<argc; i++)
diff --git a/ext/miniupnpc/upnpcommands.c b/ext/miniupnpc/upnpcommands.c
index 3988e499..d786e533 100644
--- a/ext/miniupnpc/upnpcommands.c
+++ b/ext/miniupnpc/upnpcommands.c
@@ -1,7 +1,8 @@
-/* $Id: upnpcommands.c,v 1.47 2016/03/07 12:26:48 nanard Exp $ */
-/* Project : miniupnp
+/* $Id: upnpcommands.c,v 1.48 2017/04/21 10:22:40 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * Project : miniupnp
* Author : Thomas Bernard
- * Copyright (c) 2005-2015 Thomas Bernard
+ * Copyright (c) 2005-2017 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution.
* */
@@ -372,10 +373,11 @@ UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
AddPortMappingArgs[6].val = desc?desc:"libminiupnpc";
AddPortMappingArgs[7].elt = "NewLeaseDuration";
AddPortMappingArgs[7].val = leaseDuration?leaseDuration:"0";
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "AddPortMapping", AddPortMappingArgs,
- &bufsize))) {
- free(AddPortMappingArgs);
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "AddPortMapping", AddPortMappingArgs,
+ &bufsize);
+ free(AddPortMappingArgs);
+ if(!buffer) {
return UPNPCOMMAND_HTTP_ERROR;
}
/*DisplayNameValueList(buffer, bufsize);*/
@@ -392,7 +394,6 @@ UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
ret = UPNPCOMMAND_SUCCESS;
}
ClearNameValueList(&pdata);
- free(AddPortMappingArgs);
return ret;
}
@@ -436,10 +437,11 @@ UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype,
AddPortMappingArgs[6].val = desc?desc:"libminiupnpc";
AddPortMappingArgs[7].elt = "NewLeaseDuration";
AddPortMappingArgs[7].val = leaseDuration?leaseDuration:"0";
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "AddAnyPortMapping", AddPortMappingArgs,
- &bufsize))) {
- free(AddPortMappingArgs);
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "AddAnyPortMapping", AddPortMappingArgs,
+ &bufsize);
+ free(AddPortMappingArgs);
+ if(!buffer) {
return UPNPCOMMAND_HTTP_ERROR;
}
ParseNameValue(buffer, bufsize, &pdata);
@@ -461,7 +463,6 @@ UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype,
}
}
ClearNameValueList(&pdata);
- free(AddPortMappingArgs);
return ret;
}
@@ -490,10 +491,11 @@ UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
DeletePortMappingArgs[1].val = extPort;
DeletePortMappingArgs[2].elt = "NewProtocol";
DeletePortMappingArgs[2].val = proto;
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "DeletePortMapping",
- DeletePortMappingArgs, &bufsize))) {
- free(DeletePortMappingArgs);
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "DeletePortMapping",
+ DeletePortMappingArgs, &bufsize);
+ free(DeletePortMappingArgs);
+ if(!buffer) {
return UPNPCOMMAND_HTTP_ERROR;
}
/*DisplayNameValueList(buffer, bufsize);*/
@@ -507,7 +509,6 @@ UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
ret = UPNPCOMMAND_SUCCESS;
}
ClearNameValueList(&pdata);
- free(DeletePortMappingArgs);
return ret;
}
@@ -539,10 +540,11 @@ UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype,
DeletePortMappingArgs[3].elt = "NewManage";
DeletePortMappingArgs[3].val = manage;
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "DeletePortMappingRange",
- DeletePortMappingArgs, &bufsize))) {
- free(DeletePortMappingArgs);
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "DeletePortMappingRange",
+ DeletePortMappingArgs, &bufsize);
+ free(DeletePortMappingArgs);
+ if(!buffer) {
return UPNPCOMMAND_HTTP_ERROR;
}
ParseNameValue(buffer, bufsize, &pdata);
@@ -555,7 +557,6 @@ UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype,
ret = UPNPCOMMAND_SUCCESS;
}
ClearNameValueList(&pdata);
- free(DeletePortMappingArgs);
return ret;
}
@@ -587,10 +588,11 @@ UPNP_GetGenericPortMappingEntry(const char * controlURL,
return UPNPCOMMAND_MEM_ALLOC_ERROR;
GetPortMappingArgs[0].elt = "NewPortMappingIndex";
GetPortMappingArgs[0].val = index;
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetGenericPortMappingEntry",
- GetPortMappingArgs, &bufsize))) {
- free(GetPortMappingArgs);
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetGenericPortMappingEntry",
+ GetPortMappingArgs, &bufsize);
+ free(GetPortMappingArgs);
+ if(!buffer) {
return UPNPCOMMAND_HTTP_ERROR;
}
ParseNameValue(buffer, bufsize, &pdata);
@@ -652,7 +654,6 @@ UPNP_GetGenericPortMappingEntry(const char * controlURL,
sscanf(p, "%d", &r);
}
ClearNameValueList(&pdata);
- free(GetPortMappingArgs);
return r;
}
@@ -728,10 +729,11 @@ UPNP_GetSpecificPortMappingEntry(const char * controlURL,
GetPortMappingArgs[1].val = extPort;
GetPortMappingArgs[2].elt = "NewProtocol";
GetPortMappingArgs[2].val = proto;
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetSpecificPortMappingEntry",
- GetPortMappingArgs, &bufsize))) {
- free(GetPortMappingArgs);
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetSpecificPortMappingEntry",
+ GetPortMappingArgs, &bufsize);
+ free(GetPortMappingArgs);
+ if(!buffer) {
return UPNPCOMMAND_HTTP_ERROR;
}
/*DisplayNameValueList(buffer, bufsize);*/
@@ -779,7 +781,6 @@ UPNP_GetSpecificPortMappingEntry(const char * controlURL,
}
ClearNameValueList(&pdata);
- free(GetPortMappingArgs);
return ret;
}
@@ -824,13 +825,13 @@ UPNP_GetListOfPortMappings(const char * controlURL,
GetListOfPortMappingsArgs[4].elt = "NewNumberOfPorts";
GetListOfPortMappingsArgs[4].val = numberOfPorts?numberOfPorts:"1000";
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetListOfPortMappings",
- GetListOfPortMappingsArgs, &bufsize))) {
- free(GetListOfPortMappingsArgs);
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetListOfPortMappings",
+ GetListOfPortMappingsArgs, &bufsize);
+ free(GetListOfPortMappingsArgs);
+ if(!buffer) {
return UPNPCOMMAND_HTTP_ERROR;
}
- free(GetListOfPortMappingsArgs);
/*DisplayNameValueList(buffer, bufsize);*/
ParseNameValue(buffer, bufsize, &pdata);
@@ -954,6 +955,7 @@ UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype
GetOutboundPinholeTimeoutArgs[4].val = intClient;
buffer = simpleUPnPcommand(-1, controlURL, servicetype,
"GetOutboundPinholeTimeout", GetOutboundPinholeTimeoutArgs, &bufsize);
+ free(GetOutboundPinholeTimeoutArgs);
if(!buffer)
return UPNPCOMMAND_HTTP_ERROR;
ParseNameValue(buffer, bufsize, &pdata);
@@ -972,7 +974,6 @@ UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype
*opTimeout = my_atoui(p);
}
ClearNameValueList(&pdata);
- free(GetOutboundPinholeTimeoutArgs);
return ret;
}
@@ -1031,6 +1032,7 @@ UPNP_AddPinhole(const char * controlURL, const char * servicetype,
AddPinholeArgs[5].val = leaseTime;
buffer = simpleUPnPcommand(-1, controlURL, servicetype,
"AddPinhole", AddPinholeArgs, &bufsize);
+ free(AddPinholeArgs);
if(!buffer)
return UPNPCOMMAND_HTTP_ERROR;
ParseNameValue(buffer, bufsize, &pdata);
@@ -1053,7 +1055,6 @@ UPNP_AddPinhole(const char * controlURL, const char * servicetype,
ret = UPNPCOMMAND_SUCCESS;
}
ClearNameValueList(&pdata);
- free(AddPinholeArgs);
return ret;
}
@@ -1081,6 +1082,7 @@ UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
UpdatePinholeArgs[1].val = leaseTime;
buffer = simpleUPnPcommand(-1, controlURL, servicetype,
"UpdatePinhole", UpdatePinholeArgs, &bufsize);
+ free(UpdatePinholeArgs);
if(!buffer)
return UPNPCOMMAND_HTTP_ERROR;
ParseNameValue(buffer, bufsize, &pdata);
@@ -1097,7 +1099,6 @@ UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
ret = UPNPCOMMAND_SUCCESS;
}
ClearNameValueList(&pdata);
- free(UpdatePinholeArgs);
return ret;
}
@@ -1122,6 +1123,7 @@ UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char
DeletePinholeArgs[0].val = uniqueID;
buffer = simpleUPnPcommand(-1, controlURL, servicetype,
"DeletePinhole", DeletePinholeArgs, &bufsize);
+ free(DeletePinholeArgs);
if(!buffer)
return UPNPCOMMAND_HTTP_ERROR;
/*DisplayNameValueList(buffer, bufsize);*/
@@ -1138,7 +1140,6 @@ UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char
ret = UPNPCOMMAND_SUCCESS;
}
ClearNameValueList(&pdata);
- free(DeletePinholeArgs);
return ret;
}
@@ -1163,8 +1164,11 @@ UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
CheckPinholeWorkingArgs[0].val = uniqueID;
buffer = simpleUPnPcommand(-1, controlURL, servicetype,
"CheckPinholeWorking", CheckPinholeWorkingArgs, &bufsize);
+ free(CheckPinholeWorkingArgs);
if(!buffer)
+ {
return UPNPCOMMAND_HTTP_ERROR;
+ }
ParseNameValue(buffer, bufsize, &pdata);
free(buffer); buffer = NULL;
@@ -1185,7 +1189,6 @@ UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
}
ClearNameValueList(&pdata);
- free(CheckPinholeWorkingArgs);
return ret;
}
@@ -1210,6 +1213,7 @@ UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
GetPinholePacketsArgs[0].val = uniqueID;
buffer = simpleUPnPcommand(-1, controlURL, servicetype,
"GetPinholePackets", GetPinholePacketsArgs, &bufsize);
+ free(GetPinholePacketsArgs);
if(!buffer)
return UPNPCOMMAND_HTTP_ERROR;
ParseNameValue(buffer, bufsize, &pdata);
@@ -1230,7 +1234,6 @@ UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
}
ClearNameValueList(&pdata);
- free(GetPinholePacketsArgs);
return ret;
}
diff --git a/ext/miniupnpc/upnpreplyparse.c b/ext/miniupnpc/upnpreplyparse.c
index 5de5796a..5921349d 100644
--- a/ext/miniupnpc/upnpreplyparse.c
+++ b/ext/miniupnpc/upnpreplyparse.c
@@ -1,7 +1,8 @@
-/* $Id: upnpreplyparse.c,v 1.19 2015/07/15 10:29:11 nanard Exp $ */
-/* MiniUPnP project
+/* $Id: upnpreplyparse.c,v 1.20 2017/12/12 11:26:25 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- * (c) 2006-2015 Thomas Bernard
+ * (c) 2006-2017 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
@@ -26,12 +27,12 @@ NameValueParserStartElt(void * d, const char * name, int l)
}
static void
-NameValueParserEndElt(void * d, const char * name, int l)
+NameValueParserEndElt(void * d, const char * name, int namelen)
{
struct NameValueParserData * data = (struct NameValueParserData *)d;
struct NameValue * nv;
(void)name;
- (void)l;
+ (void)namelen;
if(!data->topelt)
return;
if(strcmp(data->curelt, "NewPortListing") != 0)
@@ -104,9 +105,7 @@ ParseNameValue(const char * buffer, int bufsize,
struct NameValueParserData * data)
{
struct xmlparser parser;
- data->l_head = NULL;
- data->portListing = NULL;
- data->portListingLength = 0;
+ memset(data, 0, sizeof(struct NameValueParserData));
/* init xmlparser object */
parser.xmlstart = buffer;
parser.xmlsize = bufsize;
diff --git a/ext/misc/linux-old-glibc-compat.c b/ext/misc/linux-old-glibc-compat.c
new file mode 100644
index 00000000..6d793a2d
--- /dev/null
+++ b/ext/misc/linux-old-glibc-compat.c
@@ -0,0 +1,18 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+__asm__(".symver memcpy,memcpy@GLIBC_2.2.5");
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void *__wrap_memcpy(void *dest,const void *src,size_t n)
+{
+ return memcpy(dest,src,n);
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/include/ZeroTierDebug.h b/include/ZeroTierDebug.h
new file mode 100644
index 00000000..8e5366f0
--- /dev/null
+++ b/include/ZeroTierDebug.h
@@ -0,0 +1,114 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+/**
+ * @file
+ *
+ * Debug macros
+ */
+
+#ifndef ZT_DEBUG_H
+#define ZT_DEBUG_H
+
+#if defined(__linux__) || defined(__APPLE__)
+#include <sys/syscall.h>
+#include <pthread.h>
+#include <unistd.h>
+#endif
+
+#include <string.h>
+
+#define ZT_MSG_INFO true
+#define ZT_COLOR true
+
+// Debug output colors
+#if defined(__APPLE__)
+ #include "TargetConditionals.h"
+#endif
+#if defined(ZT_COLOR) && !defined(_WIN32) && !defined(__ANDROID__) && !defined(TARGET_OS_IPHONE) && !defined(TARGET_IPHONE_SIMULATOR) && !defined(__APP_FRAMEWORK__)
+ #define ZT_RED "\x1B[31m"
+ #define ZT_GRN "\x1B[32m"
+ #define ZT_YEL "\x1B[33m"
+ #define ZT_BLU "\x1B[34m"
+ #define ZT_MAG "\x1B[35m"
+ #define ZT_CYN "\x1B[36m"
+ #define ZT_WHT "\x1B[37m"
+ #define ZT_RESET "\x1B[0m"
+#else
+ #define ZT_RED
+ #define ZT_GRN
+ #define ZT_YEL
+ #define ZT_BLU
+ #define ZT_MAG
+ #define ZT_CYN
+ #define ZT_WHT
+ #define ZT_RESET
+#endif
+
+#define ZT_FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) // short
+
+#ifdef __linux__
+ #define ZT_THREAD_ID (long)0 // syscall(SYS_gettid)
+#endif
+#ifdef __APPLE__
+ #define ZT_THREAD_ID (long)0 // (long)gettid()
+#endif
+#ifdef _WIN32
+ #define ZT_THREAD_ID (long)0 //
+#endif
+#if defined(__JNI_LIB__)
+ #include <jni.h>
+#endif
+#if defined(__ANDROID__)
+ #include <android/log.h>
+ #define ZT_LOG_TAG "ZTSDK"
+#endif
+#if defined(ZT_TRACE)
+ #if ZT_MSG_INFO == true
+ #if defined(__ANDROID__)
+ #define DEBUG_INFO(fmt, args...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, ZT_LOG_TAG, \
+ "INFO : %17s:%5d:%20s: " fmt "\n", ZT_FILENAME, __LINE__, __FUNCTION__, ##args))
+ #endif
+ #if defined(_WIN32)
+ #define DEBUG_INFO(fmt, ...) fprintf(stderr, ZT_GRN "INFO [%ld]: %17s:%5d:%25s: " fmt "\n" \
+ ZT_RESET, ZT_THREAD_ID, ZT_FILENAME, __LINE__, __FUNCTION__, __VA_ARGS__)
+ #endif
+ #if defined(__linux__) or defined(__APPLE__)
+ #define DEBUG_INFO(fmt, args ...) fprintf(stderr, ZT_GRN "INFO [%ld]: %17s:%5d:%25s: " fmt "\n" \
+ ZT_RESET, ZT_THREAD_ID, ZT_FILENAME, __LINE__, __FUNCTION__, ##args)
+ #endif
+ #else
+ #define DEBUG_INFO(fmt, args...)
+ #endif
+#else // blank
+ #if defined(_WIN32)
+ #define DEBUG_INFO(...)
+ #else
+ #define DEBUG_INFO(fmt, args...)
+ #endif
+#endif
+
+#endif // _H
diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h
index 747e1855..6da53a73 100644
--- a/include/ZeroTierOne.h
+++ b/include/ZeroTierOne.h
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
/*
@@ -21,8 +29,8 @@
* engine.
*/
-#ifndef ZT_ZEROTIERONE_H
-#define ZT_ZEROTIERONE_H
+#ifndef ZT_ZEROTIER_API_H
+#define ZT_ZEROTIER_API_H
#include <stdint.h>
@@ -38,6 +46,31 @@
#include <sys/socket.h>
#endif /* Windows or not */
+#if defined (_MSC_VER)
+#ifdef ZT_EXPORT
+#define ZT_SDK_API __declspec(dllexport)
+#else
+#define ZT_SDK_API __declspec(dllimport)
+#if !defined(ZT_SDK)
+#ifdef _DEBUG
+#ifdef _WIN64
+#pragma comment(lib, "ZeroTierOne_x64d.lib")
+#else
+#pragma comment(lib, "ZeroTierOne_x86d.lib")
+#endif
+#else
+#ifdef _WIN64
+#pragma comment(lib, "ZeroTierOne_x64.lib")
+#else
+#pragma comment(lib, "ZeroTierOne_x86.lib")
+#endif
+#endif
+#endif
+#endif
+#else
+#define ZT_SDK_API
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -52,29 +85,47 @@ extern "C" {
#define ZT_DEFAULT_PORT 9993
/**
+ * Minimum MTU, which is the minimum allowed by IPv6 and several specs
+ */
+#define ZT_MIN_MTU 1280
+
+/**
* Maximum MTU for ZeroTier virtual networks
+ */
+#define ZT_MAX_MTU 10000
+
+/**
+ * Minimum UDP payload size allowed
+ */
+#define ZT_MIN_PHYSMTU 1400
+
+/**
+ * Default UDP payload size (physical path MTU) not including UDP and IP overhead
*
- * This is pretty much an unchangeable global constant. To make it change
- * across nodes would require logic to send ICMP packet too big messages,
- * which would complicate things. 1500 has been good enough on most LANs
- * for ages, so a larger MTU should be fine for the forseeable future. This
- * typically results in two UDP packets per single large frame. Experimental
- * results seem to show that this is good. Larger MTUs resulting in more
- * fragments seemed too brittle on slow/crummy links for no benefit.
- *
- * If this does change, also change it in tap.h in the tuntaposx code under
- * mac-tap.
- *
- * Overhead for a normal frame split into two packets:
- *
- * 1414 = 1444 (typical UDP MTU) - 28 (packet header) - 2 (ethertype)
- * 1428 = 1444 (typical UDP MTU) - 16 (fragment header)
- * SUM: 2842
- *
- * We use 2800, which leaves some room for other payload in other types of
- * messages such as multicast propagation or future support for bridging.
+ * This is 1500 - IPv6 UDP overhead - PPPoE overhead and is safe for 99.9% of
+ * all Internet links.
+ */
+#define ZT_DEFAULT_PHYSMTU 1444
+
+/**
+ * Maximum physical UDP payload
+ */
+#define ZT_MAX_PHYSPAYLOAD 10100
+
+/**
+ * Headroom for max physical MTU
*/
-#define ZT_MAX_MTU 2800
+#define ZT_MAX_HEADROOM 224
+
+/**
+ * Maximum payload MTU for UDP packets
+ */
+#define ZT_MAX_PHYSMTU (ZT_MAX_PHYSPAYLOAD + ZT_MAX_HEADROOM)
+
+/**
+ * Maximum size of a remote trace message's serialized Dictionary
+ */
+#define ZT_MAX_REMOTE_TRACE_SIZE 10000
/**
* Maximum length of network short name
@@ -124,12 +175,12 @@ extern "C" {
/**
* Maximum number of direct network paths to a given peer
*/
-#define ZT_MAX_PEER_NETWORK_PATHS 4
+#define ZT_MAX_PEER_NETWORK_PATHS 16
/**
- * Maximum number of trusted physical network paths
+ * Maximum number of path configurations that can be set
*/
-#define ZT_MAX_TRUSTED_PATHS 16
+#define ZT_MAX_CONFIGURABLE_PATHS 32
/**
* Maximum number of rules per capability
@@ -147,39 +198,6 @@ extern "C" {
#define ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH 7
/**
- * Maximum number of hops in a ZeroTier circuit test
- *
- * This is more or less the max that can be fit in a given packet (with
- * fragmentation) and only one address per hop.
- */
-#define ZT_CIRCUIT_TEST_MAX_HOPS 256
-
-/**
- * Maximum number of addresses per hop in a circuit test
- */
-#define ZT_CIRCUIT_TEST_MAX_HOP_BREADTH 8
-
-/**
- * Circuit test report flag: upstream peer authorized in path (e.g. by network COM)
- */
-#define ZT_CIRCUIT_TEST_REPORT_FLAGS_UPSTREAM_AUTHORIZED_IN_PATH 0x0000000000000001ULL
-
-/**
- * Maximum number of cluster members (and max member ID plus one)
- */
-#define ZT_CLUSTER_MAX_MEMBERS 128
-
-/**
- * Maximum number of physical ZeroTier addresses a cluster member can report
- */
-#define ZT_CLUSTER_MAX_ZT_PHYSICAL_ADDRESSES 16
-
-/**
- * Maximum allowed cluster message length in bytes
- */
-#define ZT_CLUSTER_MAX_MESSAGE_LENGTH (1500 - 48)
-
-/**
* Maximum value for link quality (min is 0)
*/
#define ZT_PATH_LINK_QUALITY_MAX 0xff
@@ -269,10 +287,75 @@ extern "C" {
*/
#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_FIN 0x0000000000000001ULL
-/**
- * A null/empty sockaddr (all zero) to signify an unspecified socket address
- */
-extern const struct sockaddr_storage ZT_SOCKADDR_NULL;
+// Fields in remote trace dictionaries
+#define ZT_REMOTE_TRACE_FIELD__EVENT "event"
+#define ZT_REMOTE_TRACE_FIELD__NODE_ID "nodeId"
+#define ZT_REMOTE_TRACE_FIELD__PACKET_ID "packetId"
+#define ZT_REMOTE_TRACE_FIELD__PACKET_VERB "packetVerb"
+#define ZT_REMOTE_TRACE_FIELD__PACKET_TRUSTED_PATH_ID "packetTrustedPathId"
+#define ZT_REMOTE_TRACE_FIELD__PACKET_TRUSTED_PATH_APPROVED "packetTrustedPathApproved"
+#define ZT_REMOTE_TRACE_FIELD__PACKET_HOPS "packetHops"
+#define ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR "remoteZtAddr"
+#define ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR "remotePhyAddr"
+#define ZT_REMOTE_TRACE_FIELD__LOCAL_ZTADDR "localZtAddr"
+#define ZT_REMOTE_TRACE_FIELD__LOCAL_PHYADDR "localPhyAddr"
+#define ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET "localSocket"
+#define ZT_REMOTE_TRACE_FIELD__IP_SCOPE "phyAddrIpScope"
+#define ZT_REMOTE_TRACE_FIELD__NETWORK_ID "networkId"
+#define ZT_REMOTE_TRACE_FIELD__SOURCE_ZTADDR "sourceZtAddr"
+#define ZT_REMOTE_TRACE_FIELD__DEST_ZTADDR "destZtAddr"
+#define ZT_REMOTE_TRACE_FIELD__SOURCE_MAC "sourceMac"
+#define ZT_REMOTE_TRACE_FIELD__DEST_MAC "destMac"
+#define ZT_REMOTE_TRACE_FIELD__ETHERTYPE "etherType"
+#define ZT_REMOTE_TRACE_FIELD__VLAN_ID "vlanId"
+#define ZT_REMOTE_TRACE_FIELD__FRAME_LENGTH "frameLength"
+#define ZT_REMOTE_TRACE_FIELD__FRAME_DATA "frameData"
+#define ZT_REMOTE_TRACE_FIELD__FILTER_FLAG_NOTEE "filterNoTee"
+#define ZT_REMOTE_TRACE_FIELD__FILTER_FLAG_INBOUND "filterInbound"
+#define ZT_REMOTE_TRACE_FIELD__FILTER_RESULT "filterResult"
+#define ZT_REMOTE_TRACE_FIELD__FILTER_BASE_RULE_LOG "filterBaseRuleLog"
+#define ZT_REMOTE_TRACE_FIELD__FILTER_CAP_RULE_LOG "filterCapRuleLog"
+#define ZT_REMOTE_TRACE_FIELD__FILTER_CAP_ID "filterMatchingCapId"
+#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE "credType"
+#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID "credId"
+#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP "credTs"
+#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_INFO "credInfo"
+#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO "credIssuedTo"
+#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_REVOCATION_TARGET "credRevocationTarget"
+#define ZT_REMOTE_TRACE_FIELD__REASON "reason"
+#define ZT_REMOTE_TRACE_FIELD__NETWORK_CONTROLLER_ID "networkControllerId"
+
+// Event types in remote traces
+#define ZT_REMOTE_TRACE_EVENT__RESETTING_PATHS_IN_SCOPE 0x1000
+#define ZT_REMOTE_TRACE_EVENT__PEER_CONFIRMING_UNKNOWN_PATH 0x1001
+#define ZT_REMOTE_TRACE_EVENT__PEER_LEARNED_NEW_PATH 0x1002
+#define ZT_REMOTE_TRACE_EVENT__PEER_REDIRECTED 0x1003
+#define ZT_REMOTE_TRACE_EVENT__PACKET_MAC_FAILURE 0x1004
+#define ZT_REMOTE_TRACE_EVENT__PACKET_INVALID 0x1005
+#define ZT_REMOTE_TRACE_EVENT__DROPPED_HELLO 0x1006
+#define ZT_REMOTE_TRACE_EVENT__OUTGOING_NETWORK_FRAME_DROPPED 0x2000
+#define ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_ACCESS_DENIED 0x2001
+#define ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_FRAME_DROPPED 0x2002
+#define ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED 0x2003
+#define ZT_REMOTE_TRACE_EVENT__CREDENTIAL_ACCEPTED 0x2004
+#define ZT_REMOTE_TRACE_EVENT__NETWORK_CONFIG_REQUEST_SENT 0x2005
+#define ZT_REMOTE_TRACE_EVENT__NETWORK_FILTER_TRACE 0x2006
+
+// Event types in remote traces in hex string form
+#define ZT_REMOTE_TRACE_EVENT__RESETTING_PATHS_IN_SCOPE_S "1000"
+#define ZT_REMOTE_TRACE_EVENT__PEER_CONFIRMING_UNKNOWN_PATH_S "1001"
+#define ZT_REMOTE_TRACE_EVENT__PEER_LEARNED_NEW_PATH_S "1002"
+#define ZT_REMOTE_TRACE_EVENT__PEER_REDIRECTED_S "1003"
+#define ZT_REMOTE_TRACE_EVENT__PACKET_MAC_FAILURE_S "1004"
+#define ZT_REMOTE_TRACE_EVENT__PACKET_INVALID_S "1005"
+#define ZT_REMOTE_TRACE_EVENT__DROPPED_HELLO_S "1006"
+#define ZT_REMOTE_TRACE_EVENT__OUTGOING_NETWORK_FRAME_DROPPED_S "2000"
+#define ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_ACCESS_DENIED_S "2001"
+#define ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_FRAME_DROPPED_S "2002"
+#define ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S "2003"
+#define ZT_REMOTE_TRACE_EVENT__CREDENTIAL_ACCEPTED_S "2004"
+#define ZT_REMOTE_TRACE_EVENT__NETWORK_CONFIG_REQUEST_SENT_S "2005"
+#define ZT_REMOTE_TRACE_EVENT__NETWORK_FILTER_TRACE_S "2006"
/****************************************************************************/
/* Structures and other types */
@@ -293,22 +376,27 @@ enum ZT_ResultCode
*/
ZT_RESULT_OK = 0,
- // Fatal errors (>0, <1000)
+ /**
+ * Call produced no error but no action was taken
+ */
+ ZT_RESULT_OK_IGNORED = 1,
+
+ // Fatal errors (>100, <1000)
/**
* Ran out of memory
*/
- ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY = 1,
+ ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY = 100,
/**
* Data store is not writable or has failed
*/
- ZT_RESULT_FATAL_ERROR_DATA_STORE_FAILED = 2,
+ ZT_RESULT_FATAL_ERROR_DATA_STORE_FAILED = 101,
/**
* Internal error (e.g. unexpected exception indicating bug or build problem)
*/
- ZT_RESULT_FATAL_ERROR_INTERNAL = 3,
+ ZT_RESULT_FATAL_ERROR_INTERNAL = 102,
// Non-fatal errors (>1000)
@@ -332,7 +420,7 @@ enum ZT_ResultCode
* @param x Result code
* @return True if result code indicates a fatal error
*/
-#define ZT_ResultCode_isFatal(x) ((((int)(x)) > 0)&&(((int)(x)) < 1000))
+#define ZT_ResultCode_isFatal(x) ((((int)(x)) >= 100)&&(((int)(x)) < 1000))
/**
* Status codes sent to status update callback when things happen
@@ -423,11 +511,61 @@ enum ZT_Event
*
* Meta-data: ZT_UserMessage structure
*/
- ZT_EVENT_USER_MESSAGE = 6
+ ZT_EVENT_USER_MESSAGE = 6,
+
+ /**
+ * Remote trace received
+ *
+ * These are generated when a VERB_REMOTE_TRACE is received. Note
+ * that any node can fling one of these at us. It is your responsibility
+ * to filter and determine if it's worth paying attention to. If it's
+ * not just drop it. Most nodes that are not active controllers ignore
+ * these, and controllers only save them if they pertain to networks
+ * with remote tracing enabled.
+ *
+ * Meta-data: ZT_RemoteTrace structure
+ */
+ ZT_EVENT_REMOTE_TRACE = 7
};
/**
+ * Payload of REMOTE_TRACE event
+ */
+typedef struct
+{
+ /**
+ * ZeroTier address of sender
+ */
+ uint64_t origin;
+
+ /**
+ * Null-terminated Dictionary containing key/value pairs sent by origin
+ *
+ * This *should* be a dictionary, but the implementation only checks
+ * that it is a valid non-empty C-style null-terminated string. Be very
+ * careful to use a well-tested parser to parse this as it represents
+ * data received from a potentially un-trusted peer on the network.
+ * Invalid payloads should be dropped.
+ *
+ * The contents of data[] may be modified.
+ */
+ char *data;
+
+ /**
+ * Length of dict[] in bytes, including terminating null
+ */
+ unsigned int len;
+} ZT_RemoteTrace;
+
+/**
* User message used with ZT_EVENT_USER_MESSAGE
+ *
+ * These are direct VL1 P2P messages for application use. Encryption and
+ * authentication in the ZeroTier protocol will guarantee the origin
+ * address and message content, but you are responsible for any other
+ * levels of authentication or access control that are required. Any node
+ * in the world can send you a user message! (Unless your network is air
+ * gapped.)
*/
typedef struct
{
@@ -611,6 +749,7 @@ enum ZT_VirtualNetworkRuleType
ZT_NETWORK_RULE_MATCH_TAGS_EQUAL = 48,
ZT_NETWORK_RULE_MATCH_TAG_SENDER = 49,
ZT_NETWORK_RULE_MATCH_TAG_RECEIVER = 50,
+ ZT_NETWORK_RULE_MATCH_INTEGER_RANGE = 51,
/**
* Maximum ID allowed for a MATCH entry in the rules table
@@ -631,7 +770,7 @@ enum ZT_VirtualNetworkRuleType
*/
typedef struct
{
- /**
+ /**
* Type and flags
*
* Bits are: NOTTTTTT
@@ -665,6 +804,20 @@ typedef struct
} ipv4;
/**
+ * Integer range match in packet payload
+ *
+ * This allows matching of ranges of integers up to 64 bits wide where
+ * the range is +/- INT32_MAX. It's packed this way so it fits in 16
+ * bytes and doesn't enlarge the overall size of this union.
+ */
+ struct {
+ uint64_t start; // integer range start
+ uint32_t end; // end of integer range (relative to start, inclusive, 0 for equality w/start)
+ uint16_t idx; // index in packet of integer
+ uint8_t format; // bits in integer (range 1-64, ((format&63)+1)) and endianness (MSB 1 for little, 0 for big)
+ } intRange;
+
+ /**
* Packet characteristic flags being matched
*/
uint64_t characteristics;
@@ -755,25 +908,6 @@ typedef struct
} v;
} ZT_VirtualNetworkRule;
-typedef struct
-{
- /**
- * 128-bit ID (GUID) of this capability
- */
- uint64_t id[2];
-
- /**
- * Expiration time (measured vs. network config timestamp issued by controller)
- */
- uint64_t expiration;
-
-
- struct {
- uint64_t from;
- uint64_t to;
- } custody[ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH];
-} ZT_VirtualNetworkCapability;
-
/**
* A route to be pushed on a virtual network
*/
@@ -943,11 +1077,6 @@ typedef struct
unsigned int mtu;
/**
- * Recommended MTU to avoid fragmentation at the physical layer (hint)
- */
- unsigned int physicalMtu;
-
- /**
* If nonzero, the network this port belongs to indicates DHCP availability
*
* This is a suggestion. The underlying implementation is free to ignore it
@@ -1017,6 +1146,21 @@ typedef struct
} ZT_VirtualNetworkList;
/**
+ * Physical path configuration
+ */
+typedef struct {
+ /**
+ * If non-zero set this physical network path to be trusted to disable encryption and authentication
+ */
+ uint64_t trustedPathId;
+
+ /**
+ * Physical path MTU from ZT_MIN_PHYSMTU and ZT_MAX_PHYSMTU or <= 0 to use default
+ */
+ int mtu;
+} ZT_PhysicalPathConfiguration;
+
+/**
* Physical network path to a peer
*/
typedef struct
@@ -1042,11 +1186,6 @@ typedef struct
uint64_t trustedPathId;
/**
- * Path link quality from 0 to 255 (always 255 if peer does not support)
- */
- int linkQuality;
-
- /**
* Is path expired?
*/
int expired;
@@ -1083,9 +1222,9 @@ typedef struct
int versionRev;
/**
- * Last measured latency in milliseconds or zero if unknown
+ * Last measured latency in milliseconds or -1 if unknown
*/
- unsigned int latency;
+ int latency;
/**
* What trust hierarchy role does this device have?
@@ -1113,267 +1252,69 @@ typedef struct
} ZT_PeerList;
/**
- * ZeroTier circuit test configuration and path
+ * ZeroTier core state objects
*/
-typedef struct {
- /**
- * Test ID -- an arbitrary 64-bit identifier
- */
- uint64_t testId;
-
+enum ZT_StateObjectType
+{
/**
- * Timestamp -- sent with test and echoed back by each reporter
+ * Null object -- ignored
*/
- uint64_t timestamp;
+ ZT_STATE_OBJECT_NULL = 0,
/**
- * Originator credential: network ID
+ * Public address and public key
*
- * If this is nonzero, a network ID will be set for this test and
- * the originator must be its primary network controller. This is
- * currently the only authorization method available, so it must
- * be set to run a test.
- */
- uint64_t credentialNetworkId;
-
- /**
- * Hops in circuit test (a.k.a. FIFO for graph traversal)
- */
- struct {
- /**
- * Hop flags (currently unused, must be zero)
- */
- unsigned int flags;
-
- /**
- * Number of addresses in this hop (max: ZT_CIRCUIT_TEST_MAX_HOP_BREADTH)
- */
- unsigned int breadth;
-
- /**
- * 40-bit ZeroTier addresses (most significant 24 bits ignored)
- */
- uint64_t addresses[ZT_CIRCUIT_TEST_MAX_HOP_BREADTH];
- } hops[ZT_CIRCUIT_TEST_MAX_HOPS];
-
- /**
- * Number of hops (max: ZT_CIRCUIT_TEST_MAX_HOPS)
- */
- unsigned int hopCount;
-
- /**
- * If non-zero, circuit test will report back at every hop
- */
- int reportAtEveryHop;
-
- /**
- * An arbitrary user-settable pointer
- */
- void *ptr;
-
- /**
- * Pointer for internal use -- initialize to zero and do not modify
- */
- void *_internalPtr;
-} ZT_CircuitTest;
-
-/**
- * Circuit test result report
- */
-typedef struct {
- /**
- * Sender of report (current hop)
- */
- uint64_t current;
-
- /**
- * Previous hop
+ * Object ID: this node's address if known, or 0 if unknown (first query)
+ * Canonical path: <HOME>/identity.public
+ * Persistence: required
*/
- uint64_t upstream;
+ ZT_STATE_OBJECT_IDENTITY_PUBLIC = 1,
/**
- * 64-bit test ID
- */
- uint64_t testId;
-
- /**
- * Timestamp from original test (echoed back at each hop)
- */
- uint64_t timestamp;
-
- /**
- * 64-bit packet ID of packet received by the reporting device
- */
- uint64_t sourcePacketId;
-
- /**
- * Flags
- */
- uint64_t flags;
-
- /**
- * ZeroTier protocol-level hop count of packet received by reporting device (>0 indicates relayed)
- */
- unsigned int sourcePacketHopCount;
-
- /**
- * Error code (currently unused, will be zero)
- */
- unsigned int errorCode;
-
- /**
- * Remote device vendor ID
- */
- enum ZT_Vendor vendor;
-
- /**
- * Remote device protocol compliance version
- */
- unsigned int protocolVersion;
-
- /**
- * Software major version
- */
- unsigned int majorVersion;
-
- /**
- * Software minor version
- */
- unsigned int minorVersion;
-
- /**
- * Software revision
- */
- unsigned int revision;
-
- /**
- * Platform / OS
- */
- enum ZT_Platform platform;
-
- /**
- * System architecture
- */
- enum ZT_Architecture architecture;
-
- /**
- * Local device address on which packet was received by reporting device
+ * Full identity with secret key
*
- * This may have ss_family equal to zero (null address) if unspecified.
+ * Object ID: this node's address if known, or 0 if unknown (first query)
+ * Canonical path: <HOME>/identity.secret
+ * Persistence: required, should be stored with restricted permissions e.g. mode 0600 on *nix
*/
- struct sockaddr_storage receivedOnLocalAddress;
+ ZT_STATE_OBJECT_IDENTITY_SECRET = 2,
/**
- * Remote address from which reporter received the test packet
+ * The planet (there is only one per... well... planet!)
*
- * This may have ss_family set to zero (null address) if unspecified.
- */
- struct sockaddr_storage receivedFromRemoteAddress;
-
- /**
- * Path link quality of physical path over which test was received
+ * Object ID: world ID of planet, or 0 if unknown (first query)
+ * Canonical path: <HOME>/planet
+ * Persistence: recommended
*/
- int receivedFromLinkQuality;
+ ZT_STATE_OBJECT_PLANET = 3,
/**
- * Next hops to which packets are being or will be sent by the reporter
+ * A moon (federated root set)
*
- * In addition to reporting back, the reporter may send the test on if
- * there are more recipients in the FIFO. If it does this, it can report
- * back the address(es) that make up the next hop and the physical address
- * for each if it has one. The physical address being null/unspecified
- * typically indicates that no direct path exists and the next packet
- * will be relayed.
- */
- struct {
- /**
- * 40-bit ZeroTier address
- */
- uint64_t address;
-
- /**
- * Physical address or null address (ss_family == 0) if unspecified or unknown
- */
- struct sockaddr_storage physicalAddress;
- } nextHops[ZT_CIRCUIT_TEST_MAX_HOP_BREADTH];
-
- /**
- * Number of next hops reported in nextHops[]
+ * Object ID: world ID of moon
+ * Canonical path: <HOME>/moons.d/<ID>.moon (16-digit hex ID)
+ * Persistence: required if moon memberships should persist
*/
- unsigned int nextHopCount;
-} ZT_CircuitTestReport;
+ ZT_STATE_OBJECT_MOON = 4,
-/**
- * A cluster member's status
- */
-typedef struct {
/**
- * This cluster member's ID (from 0 to 1-ZT_CLUSTER_MAX_MEMBERS)
- */
- unsigned int id;
-
- /**
- * Number of milliseconds since last 'alive' heartbeat message received via cluster backplane address
- */
- unsigned int msSinceLastHeartbeat;
-
- /**
- * Non-zero if cluster member is alive
- */
- int alive;
-
- /**
- * X, Y, and Z coordinates of this member (if specified, otherwise zero)
+ * Peer and related state
*
- * What these mean depends on the location scheme being used for
- * location-aware clustering. At present this is GeoIP and these
- * will be the X, Y, and Z coordinates of the location on a spherical
- * approximation of Earth where Earth's core is the origin (in km).
- * They don't have to be perfect and need only be comparable with others
- * to find shortest path via the standard vector distance formula.
- */
- int x,y,z;
-
- /**
- * Cluster member's last reported load
- */
- uint64_t load;
-
- /**
- * Number of peers
- */
- uint64_t peers;
-
- /**
- * Physical ZeroTier endpoints for this member (where peers are sent when directed here)
+ * Object ID: peer address
+ * Canonical path: <HOME>/peers.d/<ID> (10-digit address
+ * Persistence: optional, can be cleared at any time
*/
- struct sockaddr_storage zeroTierPhysicalEndpoints[ZT_CLUSTER_MAX_ZT_PHYSICAL_ADDRESSES];
+ ZT_STATE_OBJECT_PEER = 5,
/**
- * Number of physical ZeroTier endpoints this member is announcing
- */
- unsigned int numZeroTierPhysicalEndpoints;
-} ZT_ClusterMemberStatus;
-
-/**
- * ZeroTier cluster status
- */
-typedef struct {
- /**
- * My cluster member ID (a record for 'self' is included in member[])
- */
- unsigned int myId;
-
- /**
- * Number of cluster members
- */
- unsigned int clusterSize;
-
- /**
- * Cluster member statuses
+ * Network configuration
+ *
+ * Object ID: peer address
+ * Canonical path: <HOME>/networks.d/<NETWORKID>.conf (16-digit hex ID)
+ * Persistence: required if network memberships should persist
*/
- ZT_ClusterMemberStatus members[ZT_CLUSTER_MAX_MEMBERS];
-} ZT_ClusterStatus;
+ ZT_STATE_OBJECT_NETWORK_CONFIG = 6
+};
/**
* An instance of a ZeroTier One node (opaque)
@@ -1451,77 +1392,54 @@ typedef void (*ZT_EventCallback)(
const void *); /* Event payload (if applicable) */
/**
- * Function to get an object from the data store
+ * Callback for storing and/or publishing state information
*
- * Parameters: (1) object name, (2) buffer to fill, (3) size of buffer, (4)
- * index in object to start reading, (5) result parameter that must be set
- * to the actual size of the object if it exists.
+ * See ZT_StateObjectType docs for information about each state object type
+ * and when and if it needs to be persisted.
*
- * Object names can contain forward slash (/) path separators. They will
- * never contain .. or backslash (\), so this is safe to map as a Unix-style
- * path if the underlying storage permits. For security reasons we recommend
- * returning errors if .. or \ are used.
- *
- * The function must return the actual number of bytes read. If the object
- * doesn't exist, it should return -1. -2 should be returned on other errors
- * such as errors accessing underlying storage.
- *
- * If the read doesn't fit in the buffer, the max number of bytes should be
- * read. The caller may call the function multiple times to read the whole
- * object.
+ * An object of length -1 is sent to indicate that an object should be
+ * deleted.
*/
-typedef long (*ZT_DataStoreGetFunction)(
+typedef void (*ZT_StatePutFunction)(
ZT_Node *, /* Node */
void *, /* User ptr */
void *, /* Thread ptr */
- const char *,
- void *,
- unsigned long,
- unsigned long,
- unsigned long *);
+ enum ZT_StateObjectType, /* State object type */
+ const uint64_t [2], /* State object ID (if applicable) */
+ const void *, /* State object data */
+ int); /* Length of data or -1 to delete */
/**
- * Function to store an object in the data store
- *
- * Parameters: (1) node, (2) user ptr, (3) object name, (4) object data,
- * (5) object size, (6) secure? (bool).
- *
- * If secure is true, the file should be set readable and writable only
- * to the user running ZeroTier One. What this means is platform-specific.
- *
- * Name semantics are the same as the get function. This must return zero on
- * success. You can return any OS-specific error code on failure, as these
- * may be visible in logs or error messages and might aid in debugging.
+ * Callback for retrieving stored state information
*
- * If the data pointer is null, this must be interpreted as a delete
- * operation.
+ * This function should return the number of bytes actually stored to the
+ * buffer or -1 if the state object was not found or the buffer was too
+ * small to store it.
*/
-typedef int (*ZT_DataStorePutFunction)(
- ZT_Node *,
- void *,
+typedef int (*ZT_StateGetFunction)(
+ ZT_Node *, /* Node */
+ void *, /* User ptr */
void *, /* Thread ptr */
- const char *,
- const void *,
- unsigned long,
- int);
+ enum ZT_StateObjectType, /* State object type */
+ const uint64_t [2], /* State object ID (if applicable) */
+ void *, /* Buffer to store state object data */
+ unsigned int); /* Length of data buffer in bytes */
/**
- * Function to send a ZeroTier packet out over the wire
+ * Function to send a ZeroTier packet out over the physical wire (L2/L3)
*
* Parameters:
* (1) Node
* (2) User pointer
- * (3) Local interface address
+ * (3) Local socket or -1 for "all" or "any"
* (4) Remote address
* (5) Packet data
* (6) Packet length
* (7) Desired IP TTL or 0 to use default
*
- * If there is only one local interface it is safe to ignore the local
- * interface address. Otherwise if running with multiple interfaces, the
- * correct local interface should be chosen by address unless NULL. If
- * the ss_family field is zero (NULL address), a random or preferred
- * default interface should be used.
+ * If there is only one local socket, the local socket can be ignored.
+ * If the local socket is -1, the packet should be sent out from all
+ * bound local sockets or a random bound local socket.
*
* If TTL is nonzero, packets should have their IP TTL value set to this
* value if possible. If this is not possible it is acceptable to ignore
@@ -1535,7 +1453,7 @@ typedef int (*ZT_WirePacketSendFunction)(
ZT_Node *, /* Node */
void *, /* User ptr */
void *, /* Thread ptr */
- const struct sockaddr_storage *, /* Local address */
+ int64_t, /* Local socket */
const struct sockaddr_storage *, /* Remote address */
const void *, /* Packet data */
unsigned int, /* Packet length */
@@ -1548,7 +1466,7 @@ typedef int (*ZT_WirePacketSendFunction)(
* (1) Node
* (2) User pointer
* (3) ZeroTier address or 0 for none/any
- * (4) Local interface address
+ * (4) Local socket or -1 if unknown
* (5) Remote address
*
* This function must return nonzero (true) if the path should be used.
@@ -1561,16 +1479,13 @@ typedef int (*ZT_WirePacketSendFunction)(
* all configured ZeroTier interfaces and check to ensure that the supplied
* addresses will not result in ZeroTier traffic being sent over a ZeroTier
* interface (recursion).
- *
- * Obviously this is not required in configurations where this can't happen,
- * such as network containers or embedded.
*/
typedef int (*ZT_PathCheckFunction)(
ZT_Node *, /* Node */
void *, /* User ptr */
void *, /* Thread ptr */
uint64_t, /* ZeroTier address */
- const struct sockaddr_storage *, /* Local address */
+ int64_t, /* Local socket or -1 if unknown */
const struct sockaddr_storage *); /* Remote address */
/**
@@ -1611,14 +1526,14 @@ struct ZT_Node_Callbacks
long version;
/**
- * REQUIRED: Function to get objects from persistent storage
+ * REQUIRED: Function to store and/or replicate state objects
*/
- ZT_DataStoreGetFunction dataStoreGetFunction;
+ ZT_StatePutFunction statePutFunction;
/**
- * REQUIRED: Function to store objects in persistent storage
+ * REQUIRED: Function to retrieve state objects from an object store
*/
- ZT_DataStorePutFunction dataStorePutFunction;
+ ZT_StateGetFunction stateGetFunction;
/**
* REQUIRED: Function to send packets over the physical wire
@@ -1652,13 +1567,12 @@ struct ZT_Node_Callbacks
};
/**
- * Create a new ZeroTier One node
- *
- * Note that this can take a few seconds the first time it's called, as it
- * will generate an identity.
+ * Create a new ZeroTier node
*
- * TODO: should consolidate function pointers into versioned structure for
- * better API stability.
+ * This will attempt to load its identity via the state get function in the
+ * callback struct. If that fails it will generate a new identity and store
+ * it. Identity generation can take anywhere from a few hundred milliseconds
+ * to a few seconds depending on your CPU speed.
*
* @param node Result: pointer is set to new node instance on success
* @param uptr User pointer to pass to functions/callbacks
@@ -1667,7 +1581,7 @@ struct ZT_Node_Callbacks
* @param now Current clock in milliseconds
* @return OK (0) or error code if a fatal error condition has occurred
*/
-enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now);
+ZT_SDK_API enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64_t now);
/**
* Delete a node and free all resources it consumes
@@ -1677,7 +1591,7 @@ enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,void *tptr,const struct
*
* @param node Node to delete
*/
-void ZT_Node_delete(ZT_Node *node);
+ZT_SDK_API void ZT_Node_delete(ZT_Node *node);
/**
* Process a packet received from the physical wire
@@ -1685,22 +1599,22 @@ void ZT_Node_delete(ZT_Node *node);
* @param node Node instance
* @param tptr Thread pointer to pass to functions/callbacks resulting from this call
* @param now Current clock in milliseconds
- * @param localAddress Local address, or point to ZT_SOCKADDR_NULL if unspecified
+ * @param localSocket Local socket (you can use 0 if only one local socket is bound and ignore this)
* @param remoteAddress Origin of packet
* @param packetData Packet data
* @param packetLength Packet length
* @param nextBackgroundTaskDeadline Value/result: set to deadline for next call to processBackgroundTasks()
* @return OK (0) or error code if a fatal error condition has occurred
*/
-enum ZT_ResultCode ZT_Node_processWirePacket(
+ZT_SDK_API enum ZT_ResultCode ZT_Node_processWirePacket(
ZT_Node *node,
void *tptr,
- uint64_t now,
- const struct sockaddr_storage *localAddress,
+ int64_t now,
+ int64_t localSocket,
const struct sockaddr_storage *remoteAddress,
const void *packetData,
unsigned int packetLength,
- volatile uint64_t *nextBackgroundTaskDeadline);
+ volatile int64_t *nextBackgroundTaskDeadline);
/**
* Process a frame from a virtual network port (tap)
@@ -1718,10 +1632,10 @@ enum ZT_ResultCode ZT_Node_processWirePacket(
* @param nextBackgroundTaskDeadline Value/result: set to deadline for next call to processBackgroundTasks()
* @return OK (0) or error code if a fatal error condition has occurred
*/
-enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame(
+ZT_SDK_API enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame(
ZT_Node *node,
void *tptr,
- uint64_t now,
+ int64_t now,
uint64_t nwid,
uint64_t sourceMac,
uint64_t destMac,
@@ -1729,7 +1643,7 @@ enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame(
unsigned int vlanId,
const void *frameData,
unsigned int frameLength,
- volatile uint64_t *nextBackgroundTaskDeadline);
+ volatile int64_t *nextBackgroundTaskDeadline);
/**
* Perform periodic background operations
@@ -1740,7 +1654,7 @@ enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame(
* @param nextBackgroundTaskDeadline Value/result: set to deadline for next call to processBackgroundTasks()
* @return OK (0) or error code if a fatal error condition has occurred
*/
-enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,void *tptr,uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline);
+ZT_SDK_API enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,void *tptr,int64_t now,volatile int64_t *nextBackgroundTaskDeadline);
/**
* Join a network
@@ -1756,7 +1670,7 @@ enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,void *tptr,uint6
* @param uptr An arbitrary pointer to associate with this network (default: NULL)
* @return OK (0) or error code if a fatal error condition has occurred
*/
-enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr,void *tptr);
+ZT_SDK_API enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr,void *tptr);
/**
* Leave a network
@@ -1773,7 +1687,7 @@ enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr,void *tpt
* @param uptr Target pointer is set to uptr (if not NULL)
* @return OK (0) or error code if a fatal error condition has occurred
*/
-enum ZT_ResultCode ZT_Node_leave(ZT_Node *node,uint64_t nwid,void **uptr,void *tptr);
+ZT_SDK_API enum ZT_ResultCode ZT_Node_leave(ZT_Node *node,uint64_t nwid,void **uptr,void *tptr);
/**
* Subscribe to an Ethernet multicast group
@@ -1801,7 +1715,7 @@ enum ZT_ResultCode ZT_Node_leave(ZT_Node *node,uint64_t nwid,void **uptr,void *t
* @param multicastAdi Multicast ADI (least significant 32 bits only, use 0 if not needed)
* @return OK (0) or error code if a fatal error condition has occurred
*/
-enum ZT_ResultCode ZT_Node_multicastSubscribe(ZT_Node *node,void *tptr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
+ZT_SDK_API enum ZT_ResultCode ZT_Node_multicastSubscribe(ZT_Node *node,void *tptr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
/**
* Unsubscribe from an Ethernet multicast group (or all groups)
@@ -1817,7 +1731,7 @@ enum ZT_ResultCode ZT_Node_multicastSubscribe(ZT_Node *node,void *tptr,uint64_t
* @param multicastAdi Multicast ADI (least significant 32 bits only, use 0 if not needed)
* @return OK (0) or error code if a fatal error condition has occurred
*/
-enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
+ZT_SDK_API enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
/**
* Add or update a moon
@@ -1833,7 +1747,7 @@ enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node,uint64_t nwid,uint
* @param len Length of moonWorld in bytes
* @return Error if moon was invalid or failed to be added
*/
-enum ZT_ResultCode ZT_Node_orbit(ZT_Node *node,void *tptr,uint64_t moonWorldId,uint64_t moonSeed);
+ZT_SDK_API enum ZT_ResultCode ZT_Node_orbit(ZT_Node *node,void *tptr,uint64_t moonWorldId,uint64_t moonSeed);
/**
* Remove a moon (does nothing if not present)
@@ -1843,7 +1757,7 @@ enum ZT_ResultCode ZT_Node_orbit(ZT_Node *node,void *tptr,uint64_t moonWorldId,u
* @param moonWorldId World ID of moon to remove
* @return Error if anything bad happened
*/
-enum ZT_ResultCode ZT_Node_deorbit(ZT_Node *node,void *tptr,uint64_t moonWorldId);
+ZT_SDK_API enum ZT_ResultCode ZT_Node_deorbit(ZT_Node *node,void *tptr,uint64_t moonWorldId);
/**
* Get this node's 40-bit ZeroTier address
@@ -1851,7 +1765,7 @@ enum ZT_ResultCode ZT_Node_deorbit(ZT_Node *node,void *tptr,uint64_t moonWorldId
* @param node Node instance
* @return ZeroTier address (least significant 40 bits of 64-bit int)
*/
-uint64_t ZT_Node_address(ZT_Node *node);
+ZT_SDK_API uint64_t ZT_Node_address(ZT_Node *node);
/**
* Get the status of this node
@@ -1859,7 +1773,7 @@ uint64_t ZT_Node_address(ZT_Node *node);
* @param node Node instance
* @param status Buffer to fill with current node status
*/
-void ZT_Node_status(ZT_Node *node,ZT_NodeStatus *status);
+ZT_SDK_API void ZT_Node_status(ZT_Node *node,ZT_NodeStatus *status);
/**
* Get a list of known peer nodes
@@ -1870,7 +1784,7 @@ void ZT_Node_status(ZT_Node *node,ZT_NodeStatus *status);
* @param node Node instance
* @return List of known peers or NULL on failure
*/
-ZT_PeerList *ZT_Node_peers(ZT_Node *node);
+ZT_SDK_API ZT_PeerList *ZT_Node_peers(ZT_Node *node);
/**
* Get the status of a virtual network
@@ -1882,7 +1796,7 @@ ZT_PeerList *ZT_Node_peers(ZT_Node *node);
* @param nwid 64-bit network ID
* @return Network configuration or NULL if we are not a member of this network
*/
-ZT_VirtualNetworkConfig *ZT_Node_networkConfig(ZT_Node *node,uint64_t nwid);
+ZT_SDK_API ZT_VirtualNetworkConfig *ZT_Node_networkConfig(ZT_Node *node,uint64_t nwid);
/**
* Enumerate and get status of all networks
@@ -1890,7 +1804,7 @@ ZT_VirtualNetworkConfig *ZT_Node_networkConfig(ZT_Node *node,uint64_t nwid);
* @param node Node instance
* @return List of networks or NULL on failure
*/
-ZT_VirtualNetworkList *ZT_Node_networks(ZT_Node *node);
+ZT_SDK_API ZT_VirtualNetworkList *ZT_Node_networks(ZT_Node *node);
/**
* Free a query result buffer
@@ -1900,7 +1814,7 @@ ZT_VirtualNetworkList *ZT_Node_networks(ZT_Node *node);
* @param node Node instance
* @param qr Query result buffer
*/
-void ZT_Node_freeQueryResult(ZT_Node *node,void *qr);
+ZT_SDK_API void ZT_Node_freeQueryResult(ZT_Node *node,void *qr);
/**
* Add a local interface address
@@ -1924,12 +1838,12 @@ void ZT_Node_freeQueryResult(ZT_Node *node,void *qr);
* @param addr Local interface address
* @return Boolean: non-zero if address was accepted and added
*/
-int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr);
+ZT_SDK_API int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr);
/**
* Clear local interface addresses
*/
-void ZT_Node_clearLocalInterfaceAddresses(ZT_Node *node);
+ZT_SDK_API void ZT_Node_clearLocalInterfaceAddresses(ZT_Node *node);
/**
* Send a VERB_USER_MESSAGE to another ZeroTier node
@@ -1945,7 +1859,7 @@ void ZT_Node_clearLocalInterfaceAddresses(ZT_Node *node);
* @param len Length of data in bytes
* @return Boolean: non-zero on success, zero on failure
*/
-int ZT_Node_sendUserMessage(ZT_Node *node,void *tptr,uint64_t dest,uint64_t typeId,const void *data,unsigned int len);
+ZT_SDK_API int ZT_Node_sendUserMessage(ZT_Node *node,void *tptr,uint64_t dest,uint64_t typeId,const void *data,unsigned int len);
/**
* Set a network configuration master instance for this node
@@ -1962,174 +1876,17 @@ int ZT_Node_sendUserMessage(ZT_Node *node,void *tptr,uint64_t dest,uint64_t type
* @param networkConfigMasterInstance Instance of NetworkConfigMaster C++ class or NULL to disable
* @return OK (0) or error code if a fatal error condition has occurred
*/
-void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkConfigMasterInstance);
+ZT_SDK_API void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkConfigMasterInstance);
/**
- * Initiate a VL1 circuit test
- *
- * This sends an initial VERB_CIRCUIT_TEST and reports results back to the
- * supplied callback until circuitTestEnd() is called. The supplied
- * ZT_CircuitTest structure should be initially zeroed and then filled
- * in with settings and hops.
- *
- * It is the caller's responsibility to call circuitTestEnd() and then
- * to dispose of the test structure. Otherwise this node will listen
- * for results forever.
- *
- * @param node Node instance
- * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
- * @param test Test configuration
- * @param reportCallback Function to call each time a report is received
- * @return OK or error if, for example, test is too big for a packet or support isn't compiled in
- */
-enum ZT_ResultCode ZT_Node_circuitTestBegin(ZT_Node *node,void *tptr,ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *, ZT_CircuitTest *,const ZT_CircuitTestReport *));
-
-/**
- * Stop listening for results to a given circuit test
- *
- * This does not free the 'test' structure. The caller may do that
- * after calling this method to unregister it.
- *
- * Any reports that are received for a given test ID after it is
- * terminated are ignored.
- *
- * @param node Node instance
- * @param test Test configuration to unregister
- */
-void ZT_Node_circuitTestEnd(ZT_Node *node,ZT_CircuitTest *test);
-
-/**
- * Initialize cluster operation
- *
- * This initializes the internal structures and state for cluster operation.
- * It takes two function pointers. The first is to a function that can be
- * used to send data to cluster peers (mechanism is not defined by Node),
- * and the second is to a function that can be used to get the location of
- * a physical address in X,Y,Z coordinate space (e.g. as cartesian coordinates
- * projected from the center of the Earth).
- *
- * Send function takes an arbitrary pointer followed by the cluster member ID
- * to send data to, a pointer to the data, and the length of the data. The
- * maximum message length is ZT_CLUSTER_MAX_MESSAGE_LENGTH (65535). Messages
- * must be delivered whole and may be dropped or transposed, though high
- * failure rates are undesirable and can cause problems. Validity checking or
- * CRC is also not required since the Node validates the authenticity of
- * cluster messages using cryptogrphic methods and will silently drop invalid
- * messages.
- *
- * Address to location function is optional and if NULL geo-handoff is not
- * enabled (in this case x, y, and z in clusterInit are also unused). It
- * takes an arbitrary pointer followed by a physical address and three result
- * parameters for x, y, and z. It returns zero on failure or nonzero if these
- * three coordinates have been set. Coordinate space is arbitrary and can be
- * e.g. coordinates on Earth relative to Earth's center. These can be obtained
- * from latitutde and longitude with versions of the Haversine formula.
- *
- * See: http://stackoverflow.com/questions/1185408/converting-from-longitude-latitude-to-cartesian-coordinates
- *
- * Neither the send nor the address to location function should block. If the
- * address to location function does not have a location for an address, it
- * should return zero and then look up the address for future use since it
- * will be called again in (typically) 1-3 minutes.
- *
- * Note that both functions can be called from any thread from which the
- * various Node functions are called, and so must be thread safe if multiple
- * threads are being used.
- *
- * @param node Node instance
- * @param myId My cluster member ID (less than or equal to ZT_CLUSTER_MAX_MEMBERS)
- * @param zeroTierPhysicalEndpoints Preferred physical address(es) for ZeroTier clients to contact this cluster member (for peer redirect)
- * @param numZeroTierPhysicalEndpoints Number of physical endpoints in zeroTierPhysicalEndpoints[] (max allowed: 255)
- * @param x My cluster member's X location
- * @param y My cluster member's Y location
- * @param z My cluster member's Z location
- * @param sendFunction Function to be called to send data to other cluster members
- * @param sendFunctionArg First argument to sendFunction()
- * @param addressToLocationFunction Function to be called to get the location of a physical address or NULL to disable geo-handoff
- * @param addressToLocationFunctionArg First argument to addressToLocationFunction()
- * @return OK or UNSUPPORTED_OPERATION if this Node was not built with cluster support
- */
-enum ZT_ResultCode ZT_Node_clusterInit(
- ZT_Node *node,
- unsigned int myId,
- const struct sockaddr_storage *zeroTierPhysicalEndpoints,
- unsigned int numZeroTierPhysicalEndpoints,
- int x,
- int y,
- int z,
- void (*sendFunction)(void *,unsigned int,const void *,unsigned int),
- void *sendFunctionArg,
- int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *),
- void *addressToLocationFunctionArg);
-
-/**
- * Add a member to this cluster
- *
- * Calling this without having called clusterInit() will do nothing.
- *
- * @param node Node instance
- * @param memberId Member ID (must be less than or equal to ZT_CLUSTER_MAX_MEMBERS)
- * @return OK or error if clustering is disabled, ID invalid, etc.
- */
-enum ZT_ResultCode ZT_Node_clusterAddMember(ZT_Node *node,unsigned int memberId);
-
-/**
- * Remove a member from this cluster
- *
- * Calling this without having called clusterInit() will do nothing.
- *
- * @param node Node instance
- * @param memberId Member ID to remove (nothing happens if not present)
- */
-void ZT_Node_clusterRemoveMember(ZT_Node *node,unsigned int memberId);
-
-/**
- * Handle an incoming cluster state message
- *
- * The message itself contains cluster member IDs, and invalid or badly
- * addressed messages will be silently discarded.
- *
- * Calling this without having called clusterInit() will do nothing.
- *
- * @param node Node instance
- * @param msg Cluster message
- * @param len Length of cluster message
- */
-void ZT_Node_clusterHandleIncomingMessage(ZT_Node *node,const void *msg,unsigned int len);
-
-/**
- * Get the current status of the cluster from this node's point of view
- *
- * Calling this without clusterInit() or without cluster support will just
- * zero out the structure and show a cluster size of zero.
- *
- * @param node Node instance
- * @param cs Cluster status structure to fill with data
- */
-void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs);
-
-/**
- * Set trusted paths
- *
- * A trusted path is a physical network (network/bits) over which both
- * encryption and authentication can be skipped to improve performance.
- * Each trusted path must have a non-zero unique ID that is the same across
- * all participating nodes.
- *
- * We don't recommend using trusted paths at all unless you really *need*
- * near-bare-metal performance. Even on a LAN authentication and encryption
- * are never a bad thing, and anything that introduces an "escape hatch"
- * for encryption should be treated with the utmost care.
- *
- * Calling with NULL pointers for networks and ids and a count of zero clears
- * all trusted paths.
+ * Set configuration for a given physical path
*
* @param node Node instance
- * @param networks Array of [count] networks
- * @param ids Array of [count] corresponding non-zero path IDs (zero path IDs are ignored)
- * @param count Number of trusted paths-- values greater than ZT_MAX_TRUSTED_PATHS are clipped
+ * @param pathNetwork Network/CIDR of path or NULL to clear the cache and reset all paths to default
+ * @param pathConfig Path configuration or NULL to erase this entry and therefore reset it to NULL
+ * @return OK or error code
*/
-void ZT_Node_setTrustedPaths(ZT_Node *node,const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count);
+ZT_SDK_API enum ZT_ResultCode ZT_Node_setPhysicalPathConfiguration(ZT_Node *node,const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig);
/**
* Get ZeroTier One version
@@ -2138,7 +1895,7 @@ void ZT_Node_setTrustedPaths(ZT_Node *node,const struct sockaddr_storage *networ
* @param minor Result: minor version
* @param revision Result: revision
*/
-void ZT_version(int *major,int *minor,int *revision);
+ZT_SDK_API void ZT_version(int *major,int *minor,int *revision);
#ifdef __cplusplus
}
diff --git a/java/jni/Android.mk b/java/jni/Android.mk
index ebd89376..8cda2474 100644
--- a/java/jni/Android.mk
+++ b/java/jni/Android.mk
@@ -3,14 +3,21 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := ZeroTierOneJNI
-LOCAL_C_INCLUDES := $(ZT1)/include
-LOCAL_C_INCLUDES += $(ZT1)/node
+LOCAL_C_INCLUDES := \
+ $(ZT1)/include \
+ $(ZT1)/node \
+ $(ZT1)/osdep \
+ $(ZT1)/ext/miniupnpc \
+ $(ZT1)/ext/libnatpmp
+
LOCAL_LDLIBS := -llog -latomic
# LOCAL_CFLAGS := -g
+LOCAL_CFLAGS := -DZT_USE_MINIUPNPC
+
# ZeroTierOne SDK source files
LOCAL_SRC_FILES := \
- $(ZT1)/node/C25519.cpp \
+ $(ZT1)/node/C25519.cpp \
$(ZT1)/node/Capability.cpp \
$(ZT1)/node/CertificateOfMembership.cpp \
$(ZT1)/node/CertificateOfOwnership.cpp \
@@ -34,8 +41,28 @@ LOCAL_SRC_FILES := \
$(ZT1)/node/Switch.cpp \
$(ZT1)/node/Tag.cpp \
$(ZT1)/node/Topology.cpp \
- $(ZT1)/node/Utils.cpp
+ $(ZT1)/node/Trace.cpp \
+ $(ZT1)/node/Utils.cpp \
+ $(ZT1)/osdep/OSUtils.cpp \
+ $(ZT1)/osdep/PortMapper.cpp
+# libminiupnpc and libnatpmp files
+LOCAL_SRC_FILES += \
+ $(ZT1)/ext/miniupnpc/connecthostport.c \
+ $(ZT1)/ext/miniupnpc/igd_desc_parse.c \
+ $(ZT1)/ext/miniupnpc/minisoap.c \
+ $(ZT1)/ext/miniupnpc/minissdpc.c \
+ $(ZT1)/ext/miniupnpc/miniupnpc.c \
+ $(ZT1)/ext/miniupnpc/miniwget.c \
+ $(ZT1)/ext/miniupnpc/minixml.c \
+ $(ZT1)/ext/miniupnpc/portlistingparse.c \
+ $(ZT1)/ext/miniupnpc/receivedata.c \
+ $(ZT1)/ext/miniupnpc/upnpcommands.c \
+ $(ZT1)/ext/miniupnpc/upnpdev.c \
+ $(ZT1)/ext/miniupnpc/upnperrors.c \
+ $(ZT1)/ext/miniupnpc/upnpreplyparse.c \
+ $(ZT1)/ext/libnatpmp/natpmp.c \
+ $(ZT1)/ext/libnatpmp/getgateway.c
# JNI Files
LOCAL_SRC_FILES += \
diff --git a/java/jni/Application.mk b/java/jni/Application.mk
index 19891cc8..f5a87ac7 100644
--- a/java/jni/Application.mk
+++ b/java/jni/Application.mk
@@ -1,5 +1,5 @@
# NDK_TOOLCHAIN_VERSION := clang3.5
APP_STL := c++_static
-APP_CPPFLAGS := -O3 -Wall -fstack-protector -fexceptions -fno-strict-aliasing -Wno-deprecated-register -DZT_NO_TYPE_PUNNING=1
+APP_CPPFLAGS := -Wall -fstack-protector -fexceptions -fno-strict-aliasing -Wno-deprecated-register -DZT_NO_TYPE_PUNNING=1
APP_PLATFORM := android-14
APP_ABI := all
diff --git a/java/jni/ZT_jniutils.cpp b/java/jni/ZT_jniutils.cpp
index 7bdc7611..c52a2066 100644
--- a/java/jni/ZT_jniutils.cpp
+++ b/java/jni/ZT_jniutils.cpp
@@ -156,6 +156,9 @@ jobject createEvent(JNIEnv *env, ZT_Event event)
break;
case ZT_EVENT_USER_MESSAGE:
break;
+ case ZT_EVENT_REMOTE_TRACE:
+ default:
+ break;
}
jfieldID enumField = lookup.findStaticField(eventClass, fieldName.c_str(), "Lcom/zerotier/sdk/Event;");
diff --git a/java/jni/ZT_jniutils.h b/java/jni/ZT_jniutils.h
index e35d4f42..56b63179 100644
--- a/java/jni/ZT_jniutils.h
+++ b/java/jni/ZT_jniutils.h
@@ -28,17 +28,31 @@ extern "C" {
#define LOG_TAG "ZeroTierOneJNI"
-#if __ANDROID__
+#if defined(__ANDROID__)
+
#include <android/log.h>
-#define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
-#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
-#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
-#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
+
+ #if !defined(NDEBUG)
+ #define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+ #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
+ #else
+ #define LOGV(...)
+ #define LOGD(...)
+ #endif
+
+ #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
+ #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
#else
-#define LOGV(...) fprintf(stdout, __VA_ARGS__)
-#define LOGI(...) fprintf(stdout, __VA_ARGS__)
-#define LOGD(...) fprintf(stdout, __VA_ARGS__)
-#define LOGE(...) fprintf(stdout, __VA_ARGS__)
+ #if !defined(NDEBUG)
+ #define LOGV(...) fprintf(stdout, __VA_ARGS__)
+ #define LOGD(...) fprintf(stdout, __VA_ARGS__)
+ #else
+ #define LOGV(...)
+ #define LOGD(...)
+ #endif
+
+ #define LOGI(...) fprintf(stdout, __VA_ARGS__)
+ #define LOGE(...) fprintf(stdout, __VA_ARGS__)
#endif
jobject createResultObject(JNIEnv *env, ZT_ResultCode code);
diff --git a/java/jni/com_zerotierone_sdk_Node.cpp b/java/jni/com_zerotierone_sdk_Node.cpp
index defbe7f2..9a36e99b 100644
--- a/java/jni/com_zerotierone_sdk_Node.cpp
+++ b/java/jni/com_zerotierone_sdk_Node.cpp
@@ -32,6 +32,8 @@
#include <ZeroTierOne.h>
#include "Mutex.hpp"
+#include "PortMapper.hpp"
+
#include <map>
#include <string>
#include <assert.h>
@@ -58,6 +60,7 @@ namespace {
, configListener(NULL)
, pathChecker(NULL)
, callbacks(NULL)
+ , portMapper(NULL)
{
callbacks = (ZT_Node_Callbacks*)malloc(sizeof(ZT_Node_Callbacks));
memset(callbacks, 0, sizeof(ZT_Node_Callbacks));
@@ -78,9 +81,12 @@ namespace {
free(callbacks);
callbacks = NULL;
+
+ delete portMapper;
+ portMapper = NULL;
}
- uint64_t id;
+ int64_t id;
JavaVM *jvm;
@@ -95,6 +101,8 @@ namespace {
jobject pathChecker;
ZT_Node_Callbacks *callbacks;
+
+ ZeroTier::PortMapper *portMapper;
};
@@ -112,6 +120,11 @@ namespace {
JNIEnv *env = NULL;
ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+ if (ref->configListener == NULL) {
+ LOGE("configListener is NULL");
+ return -1;
+ }
+
jclass configListenerClass = env->GetObjectClass(ref->configListener);
if(configListenerClass == NULL)
{
@@ -161,13 +174,19 @@ namespace {
unsigned int frameLength)
{
LOGV("VirtualNetworkFrameFunctionCallback");
+#ifndef NDEBUG
unsigned char* local = (unsigned char*)frameData;
LOGV("Type Bytes: 0x%02x%02x", local[12], local[13]);
+#endif
JniRef *ref = (JniRef*)userData;
assert(ref->node == node);
JNIEnv *env = NULL;
ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+ if (ref->frameListener == NULL) {
+ LOGE("frameListener is NULL");
+ return;
+ }
jclass frameListenerClass = env->GetObjectClass(ref->frameListener);
if(env->ExceptionCheck() || frameListenerClass == NULL)
@@ -210,111 +229,230 @@ namespace {
void *userData,
void *threadData,
enum ZT_Event event,
- const void *data)
- {
+ const void *data) {
LOGV("EventCallback");
- JniRef *ref = (JniRef*)userData;
- if(ref->node != node && event != ZT_EVENT_UP)
- {
+ JniRef *ref = (JniRef *) userData;
+ if (ref->node != node && event != ZT_EVENT_UP) {
LOGE("Nodes not equal. ref->node %p, node %p. Event: %d", ref->node, node, event);
return;
}
JNIEnv *env = NULL;
- ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+ ref->jvm->GetEnv((void **) &env, JNI_VERSION_1_6);
+ if (ref->eventListener == NULL) {
+ LOGE("eventListener is NULL");
+ return;
+ }
jclass eventListenerClass = env->GetObjectClass(ref->eventListener);
- if(eventListenerClass == NULL)
- {
+ if (eventListenerClass == NULL) {
LOGE("Couldn't class for EventListener instance");
return;
}
jmethodID onEventMethod = lookup.findMethod(eventListenerClass,
- "onEvent", "(Lcom/zerotier/sdk/Event;)V");
- if(onEventMethod == NULL)
- {
+ "onEvent", "(Lcom/zerotier/sdk/Event;)V");
+ if (onEventMethod == NULL) {
LOGE("Couldn't find onEvent method");
return;
}
jmethodID onTraceMethod = lookup.findMethod(eventListenerClass,
- "onTrace", "(Ljava/lang/String;)V");
- if(onTraceMethod == NULL)
- {
+ "onTrace", "(Ljava/lang/String;)V");
+ if (onTraceMethod == NULL) {
LOGE("Couldn't find onTrace method");
return;
}
jobject eventObject = createEvent(env, event);
- if(eventObject == NULL)
- {
+ if (eventObject == NULL) {
return;
}
- switch(event)
- {
- case ZT_EVENT_UP:
- {
- LOGD("Event Up");
- env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
- break;
+ switch (event) {
+ case ZT_EVENT_UP: {
+ LOGD("Event Up");
+ env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
+ break;
+ }
+ case ZT_EVENT_OFFLINE: {
+ LOGD("Event Offline");
+ env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
+ break;
+ }
+ case ZT_EVENT_ONLINE: {
+ LOGD("Event Online");
+ env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
+ break;
+ }
+ case ZT_EVENT_DOWN: {
+ LOGD("Event Down");
+ env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
+ break;
+ }
+ case ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION: {
+ LOGV("Identity Collision");
+ // call onEvent()
+ env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
+ }
+ break;
+ case ZT_EVENT_TRACE: {
+ LOGV("Trace Event");
+ // call onTrace()
+ if (data != NULL) {
+ const char *message = (const char *) data;
+ jstring messageStr = env->NewStringUTF(message);
+ env->CallVoidMethod(ref->eventListener, onTraceMethod, messageStr);
+ }
+ }
+ break;
+ case ZT_EVENT_USER_MESSAGE:
+ case ZT_EVENT_REMOTE_TRACE:
+ default:
+ break;
}
- case ZT_EVENT_OFFLINE:
- {
- LOGD("Event Offline");
- env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
- break;
+ }
+
+ void StatePutFunction(
+ ZT_Node *node,
+ void *userData,
+ void *threadData,
+ enum ZT_StateObjectType type,
+ const uint64_t id[2],
+ const void *buffer,
+ int bufferLength) {
+ char p[4096] = {0};
+ bool secure = false;
+ switch (type) {
+ case ZT_STATE_OBJECT_IDENTITY_PUBLIC:
+ snprintf(p, sizeof(p), "identity.public");
+ break;
+ case ZT_STATE_OBJECT_IDENTITY_SECRET:
+ snprintf(p, sizeof(p), "identity.secret");
+ secure = true;
+ break;
+ case ZT_STATE_OBJECT_PLANET:
+ snprintf(p, sizeof(p), "planet");
+ break;
+ case ZT_STATE_OBJECT_MOON:
+ snprintf(p, sizeof(p), "moons.d/%.16llx.moon", (unsigned long long)id[0]);
+ break;
+ case ZT_STATE_OBJECT_NETWORK_CONFIG:
+ snprintf(p, sizeof(p), "networks.d/%.16llx.conf", (unsigned long long)id[0]);
+ break;
+ case ZT_STATE_OBJECT_PEER:
+ snprintf(p, sizeof(p), "peers.d/%.10llx", (unsigned long long)id[0]);
+ break;
+ default:
+ return;
}
- case ZT_EVENT_ONLINE:
- {
- LOGD("Event Online");
- env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
- break;
+
+ if (strlen(p) < 1) {
+ return;
+ }
+
+ JniRef *ref = (JniRef*)userData;
+ JNIEnv *env = NULL;
+ ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+
+ if (ref->dataStorePutListener == NULL) {
+ LOGE("dataStorePutListener is NULL");
+ return;
}
- case ZT_EVENT_DOWN:
+
+ jclass dataStorePutClass = env->GetObjectClass(ref->dataStorePutListener);
+ if (dataStorePutClass == NULL)
{
- LOGD("Event Down");
- env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
- break;
+ LOGE("Couldn't find class for DataStorePutListener instance");
+ return;
}
- case ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION:
+
+ jmethodID dataStorePutCallbackMethod = lookup.findMethod(
+ dataStorePutClass,
+ "onDataStorePut",
+ "(Ljava/lang/String;[BZ)I");
+ if(dataStorePutCallbackMethod == NULL)
{
- LOGV("Identity Collision");
- // call onEvent()
- env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
+ LOGE("Couldn't find onDataStorePut method");
+ return;
}
- break;
- case ZT_EVENT_TRACE:
+
+ jmethodID deleteMethod = lookup.findMethod(dataStorePutClass,
+ "onDelete", "(Ljava/lang/String;)I");
+ if(deleteMethod == NULL)
{
- LOGV("Trace Event");
- // call onTrace()
- if(data != NULL)
+ LOGE("Couldn't find onDelete method");
+ return;
+ }
+
+ jstring nameStr = env->NewStringUTF(p);
+
+ if (bufferLength >= 0) {
+ LOGD("JNI: Write file: %s", p);
+ // set operation
+ jbyteArray bufferObj = env->NewByteArray(bufferLength);
+ if(env->ExceptionCheck() || bufferObj == NULL)
{
- const char* message = (const char*)data;
- jstring messageStr = env->NewStringUTF(message);
- env->CallVoidMethod(ref->eventListener, onTraceMethod, messageStr);
+ LOGE("Error creating byte array buffer!");
+ return;
}
- }
- break;
- case ZT_EVENT_USER_MESSAGE:
- break;
+
+ env->SetByteArrayRegion(bufferObj, 0, bufferLength, (jbyte*)buffer);
+
+ env->CallIntMethod(ref->dataStorePutListener,
+ dataStorePutCallbackMethod,
+ nameStr, bufferObj, secure);
+ } else {
+ LOGD("JNI: Delete file: %s", p);
+ env->CallIntMethod(ref->dataStorePutListener, deleteMethod, nameStr);
}
}
- long DataStoreGetFunction(ZT_Node *node,
- void *userData,
- void *threadData,
- const char *objectName,
- void *buffer,
- unsigned long bufferSize,
- unsigned long bufferIndex,
- unsigned long *out_objectSize)
- {
+ int StateGetFunction(
+ ZT_Node *node,
+ void *userData,
+ void *threadData,
+ ZT_StateObjectType type,
+ const uint64_t id[2],
+ void *buffer,
+ unsigned int bufferLength) {
+ char p[4096] = {0};
+ switch (type) {
+ case ZT_STATE_OBJECT_IDENTITY_PUBLIC:
+ snprintf(p, sizeof(p), "identity.public");
+ break;
+ case ZT_STATE_OBJECT_IDENTITY_SECRET:
+ snprintf(p, sizeof(p), "identity.secret");
+ break;
+ case ZT_STATE_OBJECT_PLANET:
+ snprintf(p, sizeof(p), "planet");
+ break;
+ case ZT_STATE_OBJECT_MOON:
+ snprintf(p, sizeof(p), "moons.d/%.16llx.moon", (unsigned long long)id[0]);
+ break;
+ case ZT_STATE_OBJECT_NETWORK_CONFIG:
+ snprintf(p, sizeof(p), "networks.d/%.16llx.conf", (unsigned long long)id[0]);
+ break;
+ case ZT_STATE_OBJECT_PEER:
+ snprintf(p, sizeof(p), "peers.d/%.10llx", (unsigned long long)id[0]);
+ break;
+ default:
+ return -1;
+ }
+
+ if (strlen(p) < 1) {
+ return -1;
+ }
+
JniRef *ref = (JniRef*)userData;
JNIEnv *env = NULL;
ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+ if (ref->dataStoreGetListener == NULL) {
+ LOGE("dataStoreGetListener is NULL");
+ return -2;
+ }
+
jclass dataStoreGetClass = env->GetObjectClass(ref->dataStoreGetListener);
if(dataStoreGetClass == NULL)
{
@@ -323,142 +461,69 @@ namespace {
}
jmethodID dataStoreGetCallbackMethod = lookup.findMethod(
- dataStoreGetClass,
- "onDataStoreGet",
- "(Ljava/lang/String;[BJ[J)J");
+ dataStoreGetClass,
+ "onDataStoreGet",
+ "(Ljava/lang/String;[B)J");
if(dataStoreGetCallbackMethod == NULL)
{
LOGE("Couldn't find onDataStoreGet method");
return -2;
}
- jstring nameStr = env->NewStringUTF(objectName);
+ jstring nameStr = env->NewStringUTF(p);
if(nameStr == NULL)
{
LOGE("Error creating name string object");
return -2; // out of memory
}
- jbyteArray bufferObj = env->NewByteArray(bufferSize);
+ jbyteArray bufferObj = env->NewByteArray(bufferLength);
if(bufferObj == NULL)
{
- LOGE("Error creating byte[] buffer of size: %lu", bufferSize);
+ LOGE("Error creating byte[] buffer of size: %u", bufferLength);
return -2;
}
- jlongArray objectSizeObj = env->NewLongArray(1);
- if(objectSizeObj == NULL)
- {
- LOGE("Error creating long[1] array for actual object size");
- return -2; // couldn't create long[1] array
- }
+ LOGV("Calling onDataStoreGet(%s, %p)", p, buffer);
- LOGV("Calling onDataStoreGet(%s, %p, %lu, %p)",
- objectName, buffer, bufferIndex, objectSizeObj);
+ int retval = (int)env->CallLongMethod(
+ ref->dataStoreGetListener,
+ dataStoreGetCallbackMethod,
+ nameStr,
+ bufferObj);
- long retval = (long)env->CallLongMethod(
- ref->dataStoreGetListener, dataStoreGetCallbackMethod,
- nameStr, bufferObj, (jlong)bufferIndex, objectSizeObj);
+ LOGV("onDataStoreGet returned %d", retval);
if(retval > 0)
{
void *data = env->GetPrimitiveArrayCritical(bufferObj, NULL);
memcpy(buffer, data, retval);
env->ReleasePrimitiveArrayCritical(bufferObj, data, 0);
-
- jlong *objSize = (jlong*)env->GetPrimitiveArrayCritical(objectSizeObj, NULL);
- *out_objectSize = (unsigned long)objSize[0];
- env->ReleasePrimitiveArrayCritical(objectSizeObj, objSize, 0);
}
- LOGV("Out Object Size: %lu", *out_objectSize);
-
return retval;
}
- int DataStorePutFunction(ZT_Node *node,
- void *userData,
- void *threadData,
- const char *objectName,
- const void *buffer,
- unsigned long bufferSize,
- int secure)
- {
- JniRef *ref = (JniRef*)userData;
- JNIEnv *env = NULL;
- ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
-
-
- jclass dataStorePutClass = env->GetObjectClass(ref->dataStorePutListener);
- if(dataStorePutClass == NULL)
- {
- LOGE("Couldn't find class for DataStorePutListener instance");
- return -1;
- }
-
- jmethodID dataStorePutCallbackMethod = lookup.findMethod(
- dataStorePutClass,
- "onDataStorePut",
- "(Ljava/lang/String;[BZ)I");
- if(dataStorePutCallbackMethod == NULL)
- {
- LOGE("Couldn't find onDataStorePut method");
- return -2;
- }
-
- jmethodID deleteMethod = lookup.findMethod(dataStorePutClass,
- "onDelete", "(Ljava/lang/String;)I");
- if(deleteMethod == NULL)
- {
- LOGE("Couldn't find onDelete method");
- return -3;
- }
-
- jstring nameStr = env->NewStringUTF(objectName);
-
- if(buffer == NULL)
- {
- LOGD("JNI: Delete file: %s", objectName);
- // delete operation
- return env->CallIntMethod(
- ref->dataStorePutListener, deleteMethod, nameStr);
- }
- else
- {
- LOGD("JNI: Write file: %s", objectName);
- // set operation
- jbyteArray bufferObj = env->NewByteArray(bufferSize);
- if(env->ExceptionCheck() || bufferObj == NULL)
- {
- LOGE("Error creating byte array buffer!");
- return -4;
- }
-
- env->SetByteArrayRegion(bufferObj, 0, bufferSize, (jbyte*)buffer);
- bool bsecure = secure != 0;
-
- return env->CallIntMethod(ref->dataStorePutListener,
- dataStorePutCallbackMethod,
- nameStr, bufferObj, bsecure);
- }
- }
-
int WirePacketSendFunction(ZT_Node *node,
void *userData,
void *threadData,
- const struct sockaddr_storage *localAddress,
+ int64_t localSocket,
const struct sockaddr_storage *remoteAddress,
const void *buffer,
unsigned int bufferSize,
unsigned int ttl)
{
- LOGV("WirePacketSendFunction(%p, %p, %p, %d)", localAddress, remoteAddress, buffer, bufferSize);
+ LOGV("WirePacketSendFunction(%lld, %p, %p, %d)", (long long)localSocket, remoteAddress, buffer, bufferSize);
JniRef *ref = (JniRef*)userData;
assert(ref->node == node);
JNIEnv *env = NULL;
ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+ if (ref->packetSender == NULL) {
+ LOGE("packetSender is NULL");
+ return -1;
+ }
jclass packetSenderClass = env->GetObjectClass(ref->packetSender);
if(packetSenderClass == NULL)
@@ -468,23 +533,17 @@ namespace {
}
jmethodID packetSenderCallbackMethod = lookup.findMethod(packetSenderClass,
- "onSendPacketRequested", "(Ljava/net/InetSocketAddress;Ljava/net/InetSocketAddress;[BI)I");
+ "onSendPacketRequested", "(JLjava/net/InetSocketAddress;[BI)I");
if(packetSenderCallbackMethod == NULL)
{
LOGE("Couldn't find onSendPacketRequested method");
return -2;
}
- jobject localAddressObj = NULL;
- if(memcmp(localAddress, &ZT_SOCKADDR_NULL, sizeof(sockaddr_storage)) != 0)
- {
- localAddressObj = newInetSocketAddress(env, *localAddress);
- }
-
jobject remoteAddressObj = newInetSocketAddress(env, *remoteAddress);
jbyteArray bufferObj = env->NewByteArray(bufferSize);
env->SetByteArrayRegion(bufferObj, 0, bufferSize, (jbyte*)buffer);
- int retval = env->CallIntMethod(ref->packetSender, packetSenderCallbackMethod, localAddressObj, remoteAddressObj, bufferObj);
+ int retval = env->CallIntMethod(ref->packetSender, packetSenderCallbackMethod, localSocket, remoteAddressObj, bufferObj);
LOGV("JNI Packet Sender returned: %d", retval);
return retval;
@@ -494,7 +553,7 @@ namespace {
void *userPtr,
void *threadPtr,
uint64_t address,
- const struct sockaddr_storage *localAddress,
+ int64_t localSocket,
const struct sockaddr_storage *remoteAddress)
{
JniRef *ref = (JniRef*)userPtr;
@@ -515,26 +574,22 @@ namespace {
}
jmethodID pathCheckCallbackMethod = lookup.findMethod(pathCheckerClass,
- "onPathCheck", "(JLjava/net/InetSocketAddress;Ljava/net/InetSocketAddress;)Z");
+ "onPathCheck", "(JJLjava/net/InetSocketAddress;)Z");
if(pathCheckCallbackMethod == NULL)
{
LOGE("Couldn't find onPathCheck method implementation");
return true;
}
- jobject localAddressObj = NULL;
+ struct sockaddr_storage nullAddress = {0};
jobject remoteAddressObj = NULL;
- if(memcmp(localAddress, &ZT_SOCKADDR_NULL, sizeof(sockaddr_storage)) != 0)
- {
- localAddressObj = newInetSocketAddress(env, *localAddress);
- }
- if(memcmp(remoteAddress, &ZT_SOCKADDR_NULL, sizeof(sockaddr_storage)) != 0)
+ if(memcmp(remoteAddress, &nullAddress, sizeof(sockaddr_storage)) != 0)
{
remoteAddressObj = newInetSocketAddress(env, *remoteAddress);
}
- return env->CallBooleanMethod(ref->pathChecker, pathCheckCallbackMethod, address, localAddressObj, remoteAddressObj);
+ return env->CallBooleanMethod(ref->pathChecker, pathCheckCallbackMethod, address, localSocket, remoteAddressObj);
}
int PathLookupFunction(ZT_Node *node,
@@ -649,11 +704,11 @@ namespace {
return true;
}
- typedef std::map<uint64_t, JniRef*> NodeMap;
+ typedef std::map<int64_t, JniRef*> NodeMap;
static NodeMap nodeMap;
ZeroTier::Mutex nodeMapMutex;
- ZT_Node* findNode(uint64_t nodeId)
+ ZT_Node* findNode(int64_t nodeId)
{
ZeroTier::Mutex::Lock lock(nodeMapMutex);
NodeMap::iterator found = nodeMap.find(nodeId);
@@ -691,7 +746,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init(
ZT_Node *node;
JniRef *ref = new JniRef;
- ref->id = (uint64_t)now;
+ ref->id = (int64_t)now;
env->GetJavaVM(&ref->jvm);
jclass cls = env->GetObjectClass(obj);
@@ -795,8 +850,8 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init(
ref->pathChecker = env->NewGlobalRef(tmp);
}
- ref->callbacks->dataStoreGetFunction = &DataStoreGetFunction;
- ref->callbacks->dataStorePutFunction = &DataStorePutFunction;
+ ref->callbacks->stateGetFunction = &StateGetFunction;
+ ref->callbacks->statePutFunction = &StatePutFunction;
ref->callbacks->wirePacketSendFunction = &WirePacketSendFunction;
ref->callbacks->virtualNetworkFrameFunction = &VirtualNetworkFrameFunctionCallback;
ref->callbacks->virtualNetworkConfigFunction = &VirtualNetworkConfigFunctionCallback;
@@ -809,7 +864,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init(
ref,
NULL,
ref->callbacks,
- (uint64_t)now);
+ (int64_t)now);
if(rc != ZT_RESULT_OK)
{
@@ -825,11 +880,17 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init(
return resultObject;
}
+ uint64_t nodeId = ZT_Node_address(node);
+ if (nodeId != 0) {
+ char uniqueName[64];
+ snprintf(uniqueName, sizeof(uniqueName), "ZeroTier Android/%.10llx@%u", (unsigned long long)nodeId, 9993);
+ ref->portMapper = new ZeroTier::PortMapper(9993, uniqueName);
+ }
+
ZeroTier::Mutex::Lock lock(nodeMapMutex);
ref->node = node;
nodeMap.insert(std::make_pair(ref->id, ref));
-
return resultObject;
}
@@ -842,7 +903,7 @@ JNIEXPORT void JNICALL Java_com_zerotier_sdk_Node_node_1delete(
JNIEnv *env, jobject obj, jlong id)
{
LOGV("Destroying ZT_Node struct");
- uint64_t nodeId = (uint64_t)id;
+ int64_t nodeId = (int64_t)id;
NodeMap::iterator found;
{
@@ -883,7 +944,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame(
jbyteArray in_frameData,
jlongArray out_nextBackgroundTaskDeadline)
{
- uint64_t nodeId = (uint64_t) id;
+ int64_t nodeId = (int64_t) id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
@@ -899,7 +960,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame(
return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL);
}
- uint64_t now = (uint64_t)in_now;
+ int64_t now = (int64_t)in_now;
uint64_t nwid = (uint64_t)in_nwid;
uint64_t sourceMac = (uint64_t)in_sourceMac;
uint64_t destMac = (uint64_t)in_destMac;
@@ -912,7 +973,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame(
memcpy(localData, frameData, frameLength);
env->ReleasePrimitiveArrayCritical(in_frameData, frameData, 0);
- uint64_t nextBackgroundTaskDeadline = 0;
+ int64_t nextBackgroundTaskDeadline = 0;
ZT_ResultCode rc = ZT_Node_processVirtualNetworkFrame(
node,
@@ -937,18 +998,18 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame(
/*
* Class: com_zerotier_sdk_Node
* Method: processWirePacket
- * Signature: (JJLjava/net/InetSocketAddress;I[B[J)Lcom/zerotier/sdk/ResultCode;
+ * Signature: (JJJLjava/net/InetSocketAddress;I[B[J)Lcom/zerotier/sdk/ResultCode;
*/
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket(
JNIEnv *env, jobject obj,
jlong id,
jlong in_now,
- jobject in_localAddress,
+ jlong in_localSocket,
jobject in_remoteAddress,
jbyteArray in_packetData,
jlongArray out_nextBackgroundTaskDeadline)
{
- uint64_t nodeId = (uint64_t) id;
+ int64_t nodeId = (int64_t) id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
@@ -957,14 +1018,14 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket(
return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL);
}
- unsigned int nbtd_len = env->GetArrayLength(out_nextBackgroundTaskDeadline);
+ unsigned int nbtd_len = (unsigned int)env->GetArrayLength(out_nextBackgroundTaskDeadline);
if(nbtd_len < 1)
{
LOGE("nbtd_len < 1");
return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL);
}
- uint64_t now = (uint64_t)in_now;
+ int64_t now = (int64_t)in_now;
// get the java.net.InetSocketAddress class and getAddress() method
jclass inetAddressClass = lookup.findClass("java/net/InetAddress");
@@ -992,12 +1053,6 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket(
jmethodID inetSockGetAddressMethod = lookup.findMethod(
InetSocketAddressClass, "getAddress", "()Ljava/net/InetAddress;");
- jobject localAddrObj = NULL;
- if(in_localAddress != NULL)
- {
- localAddrObj = env->CallObjectMethod(in_localAddress, inetSockGetAddressMethod);
- }
-
jobject remoteAddrObject = env->CallObjectMethod(in_remoteAddress, inetSockGetAddressMethod);
if(remoteAddrObject == NULL)
@@ -1034,47 +1089,6 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket(
unsigned int addrSize = env->GetArrayLength(remoteAddressArray);
- sockaddr_storage localAddress = {};
-
- if(localAddrObj == NULL)
- {
- localAddress = ZT_SOCKADDR_NULL;
- }
- else
- {
- int localPort = env->CallIntMethod(in_localAddress, inetSock_getPort);
- jbyteArray localAddressArray = (jbyteArray)env->CallObjectMethod(localAddrObj, getAddressMethod);
- if(localAddressArray != NULL)
- {
-
- unsigned int localAddrSize = env->GetArrayLength(localAddressArray);
- jbyte *addr = (jbyte*)env->GetPrimitiveArrayCritical(localAddressArray, NULL);
-
- if(localAddrSize == 16)
- {
- sockaddr_in6 ipv6 = {};
- ipv6.sin6_family = AF_INET6;
- ipv6.sin6_port = htons(localPort);
- memcpy(ipv6.sin6_addr.s6_addr, addr, 16);
- memcpy(&localAddress, &ipv6, sizeof(sockaddr_in6));
- }
- else if(localAddrSize)
- {
- // IPV4 address
- sockaddr_in ipv4 = {};
- ipv4.sin_family = AF_INET;
- ipv4.sin_port = htons(localPort);
- memcpy(&ipv4.sin_addr, addr, 4);
- memcpy(&localAddress, &ipv4, sizeof(sockaddr_in));
- }
- else
- {
- localAddress = ZT_SOCKADDR_NULL;
- }
- env->ReleasePrimitiveArrayCritical(localAddressArray, addr, 0);
- }
- }
-
// get the address bytes
jbyte *addr = (jbyte*)env->GetPrimitiveArrayCritical(remoteAddressArray, NULL);
sockaddr_storage remoteAddress = {};
@@ -1106,7 +1120,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket(
}
env->ReleasePrimitiveArrayCritical(remoteAddressArray, addr, 0);
- unsigned int packetLength = env->GetArrayLength(in_packetData);
+ unsigned int packetLength = (unsigned int)env->GetArrayLength(in_packetData);
if(packetLength == 0)
{
LOGE("Empty packet?!?");
@@ -1117,13 +1131,13 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket(
memcpy(localData, packetData, packetLength);
env->ReleasePrimitiveArrayCritical(in_packetData, packetData, 0);
- uint64_t nextBackgroundTaskDeadline = 0;
+ int64_t nextBackgroundTaskDeadline = 0;
ZT_ResultCode rc = ZT_Node_processWirePacket(
node,
NULL,
now,
- &localAddress,
+ in_localSocket,
&remoteAddress,
localData,
packetLength,
@@ -1153,7 +1167,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processBackgroundTasks(
jlong in_now,
jlongArray out_nextBackgroundTaskDeadline)
{
- uint64_t nodeId = (uint64_t) id;
+ int64_t nodeId = (int64_t) id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
@@ -1167,8 +1181,8 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processBackgroundTasks(
return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL);
}
- uint64_t now = (uint64_t)in_now;
- uint64_t nextBackgroundTaskDeadline = 0;
+ int64_t now = (int64_t)in_now;
+ int64_t nextBackgroundTaskDeadline = 0;
ZT_ResultCode rc = ZT_Node_processBackgroundTasks(node, NULL, now, &nextBackgroundTaskDeadline);
@@ -1187,7 +1201,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processBackgroundTasks(
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_join(
JNIEnv *env, jobject obj, jlong id, jlong in_nwid)
{
- uint64_t nodeId = (uint64_t) id;
+ int64_t nodeId = (int64_t) id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
@@ -1210,7 +1224,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_join(
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_leave(
JNIEnv *env, jobject obj, jlong id, jlong in_nwid)
{
- uint64_t nodeId = (uint64_t) id;
+ int64_t nodeId = (int64_t) id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
@@ -1237,7 +1251,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_multicastSubscribe(
jlong in_multicastGroup,
jlong in_multicastAdi)
{
- uint64_t nodeId = (uint64_t) id;
+ int64_t nodeId = (int64_t) id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
@@ -1267,7 +1281,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_multicastUnsubscribe(
jlong in_multicastGroup,
jlong in_multicastAdi)
{
- uint64_t nodeId = (uint64_t) id;
+ int64_t nodeId = (int64_t) id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
@@ -1296,7 +1310,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_orbit(
jlong in_moonWorldId,
jlong in_moonSeed)
{
- uint64_t nodeId = (uint64_t)id;
+ int64_t nodeId = (int64_t)id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
@@ -1320,7 +1334,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_deorbit(
jlong id,
jlong in_moonWorldId)
{
- uint64_t nodeId = (uint64_t)id;
+ int64_t nodeId = (int64_t)id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
@@ -1341,7 +1355,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_deorbit(
JNIEXPORT jlong JNICALL Java_com_zerotier_sdk_Node_address(
JNIEnv *env , jobject obj, jlong id)
{
- uint64_t nodeId = (uint64_t) id;
+ int64_t nodeId = (int64_t) id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
@@ -1361,7 +1375,7 @@ JNIEXPORT jlong JNICALL Java_com_zerotier_sdk_Node_address(
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_status
(JNIEnv *env, jobject obj, jlong id)
{
- uint64_t nodeId = (uint64_t) id;
+ int64_t nodeId = (int64_t) id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
@@ -1453,7 +1467,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_status
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_networkConfig(
JNIEnv *env, jobject obj, jlong id, jlong nwid)
{
- uint64_t nodeId = (uint64_t) id;
+ int64_t nodeId = (int64_t) id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
@@ -1495,7 +1509,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_version(
JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_peers(
JNIEnv *env, jobject obj, jlong id)
{
- uint64_t nodeId = (uint64_t) id;
+ int64_t nodeId = (int64_t) id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
@@ -1564,7 +1578,7 @@ JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_peers(
JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_networks(
JNIEnv *env, jobject obj, jlong id)
{
- uint64_t nodeId = (uint64_t) id;
+ int64_t nodeId = (int64_t) id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
diff --git a/java/jni/com_zerotierone_sdk_Node.h b/java/jni/com_zerotierone_sdk_Node.h
index 7c1011ab..8487d8af 100644
--- a/java/jni/com_zerotierone_sdk_Node.h
+++ b/java/jni/com_zerotierone_sdk_Node.h
@@ -37,7 +37,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame
* Signature: (JJLjava/net/InetSockAddress;Ljava/net/InetSockAddress;[B[J)Lcom/zerotier/sdk/ResultCode;
*/
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket
- (JNIEnv *, jobject, jlong, jlong, jobject, jobject, jbyteArray, jlongArray);
+ (JNIEnv *, jobject, jlong, jlong, jlong, jobject, jbyteArray, jlongArray);
/*
* Class: com_zerotier_sdk_Node
diff --git a/java/src/com/zerotier/sdk/DataStoreGetListener.java b/java/src/com/zerotier/sdk/DataStoreGetListener.java
index b525be63..317511e0 100644
--- a/java/src/com/zerotier/sdk/DataStoreGetListener.java
+++ b/java/src/com/zerotier/sdk/DataStoreGetListener.java
@@ -46,13 +46,9 @@ public interface DataStoreGetListener {
*
* @param name Name of the object in the data store
* @param out_buffer buffer to put the object in
- * @param bufferIndex index in the object to start reading
- * @param out_objectSize long[1] to be set to the actual size of the object if it exists.
- * @return the actual number of bytes read.
+ * @return size of the object
*/
public long onDataStoreGet(
String name,
- byte[] out_buffer,
- long bufferIndex,
- long[] out_objectSize);
+ byte[] out_buffer);
}
diff --git a/java/src/com/zerotier/sdk/Node.java b/java/src/com/zerotier/sdk/Node.java
index 8e7d44e7..ef6ac9d2 100644
--- a/java/src/com/zerotier/sdk/Node.java
+++ b/java/src/com/zerotier/sdk/Node.java
@@ -173,12 +173,12 @@ public class Node {
*/
public ResultCode processWirePacket(
long now,
- InetSocketAddress localAddress,
+ long localSocket,
InetSocketAddress remoteAddress,
byte[] packetData,
long[] nextBackgroundTaskDeadline) {
return processWirePacket(
- nodeId, now, localAddress, remoteAddress, packetData,
+ nodeId, now, localSocket, remoteAddress, packetData,
nextBackgroundTaskDeadline);
}
@@ -426,7 +426,7 @@ public class Node {
private native ResultCode processWirePacket(
long nodeId,
long now,
- InetSocketAddress localAddress,
+ long localSocket,
InetSocketAddress remoteAddress,
byte[] packetData,
long[] nextBackgroundTaskDeadline);
diff --git a/java/src/com/zerotier/sdk/PacketSender.java b/java/src/com/zerotier/sdk/PacketSender.java
index 22893ec7..06ec01bc 100644
--- a/java/src/com/zerotier/sdk/PacketSender.java
+++ b/java/src/com/zerotier/sdk/PacketSender.java
@@ -37,13 +37,13 @@ public interface PacketSender {
* on failure. Note that success does not (of course) guarantee packet
* delivery. It only means that the packet appears to have been sent.</p>
*
- * @param localAddr {@link InetSocketAddress} to send from. Set to null if not specified.
+ * @param localSocket socket file descriptor to send from. Set to -1 if not specified.
* @param remoteAddr {@link InetSocketAddress} to send to
* @param packetData data to send
* @return 0 on success, any error code on failure.
*/
public int onSendPacketRequested(
- InetSocketAddress localAddr,
+ long localSocket,
InetSocketAddress remoteAddr,
byte[] packetData,
int ttl);
diff --git a/java/src/com/zerotier/sdk/PathChecker.java b/java/src/com/zerotier/sdk/PathChecker.java
index 3e02f112..6bf31df2 100644
--- a/java/src/com/zerotier/sdk/PathChecker.java
+++ b/java/src/com/zerotier/sdk/PathChecker.java
@@ -26,10 +26,10 @@ public interface PathChecker {
* such as network containers or embedded.
*
* @param ztAddress ZeroTier address or 0 for none/any
- * @param localAddress Local interface address
+ * @param localSocket Local interface socket. -1 if unspecified
* @param remoteAddress remote address
*/
- boolean onPathCheck(long ztAddress, InetSocketAddress localAddress, InetSocketAddress remoteAddress);
+ boolean onPathCheck(long ztAddress, long localSocket, InetSocketAddress remoteAddress);
/**
* Function to get physical addresses for ZeroTier peers
diff --git a/macui/ZeroTier One/about.html b/macui/ZeroTier One/about.html
index 4fa41d7b..09f6eb36 100644
--- a/macui/ZeroTier One/about.html
+++ b/macui/ZeroTier One/about.html
@@ -1,65 +1,58 @@
<html>
- <head>
- <style type="text/css">
- html,body {
- background: #ffffff;
- margin: 0;
- padding: 0;
- font-family: "Helvetica";
- font-size: 12pt;
- height: 100%;
- width: 100%;
- }
- div.icon {
- background: #ffb354;
- color: #000000;
- font-size: 100pt;
- border-radius: 2.5rem;
- display: inline-block;
- width: 1.3em;
- height: 1.3em;
- padding: 0;
- margin: 0;
- line-height: 1.4em;
- vertical-align: middle;
- text-align: center;
- }
- div.icon_container {
- font-weight: bold;
- }
- a,p,h1,h2,h3,h4,span,div,strong,center,lead,nav,ol,ul,li,img,button,input,textarea,form {
- font-family: "Clear Sans Light","Helvetica Neue","Helvetica",sans-serif !important;
- -webkit-font-smoothing: antialiased;
- }
- .code {
- font-family: "Menlo","Consolas","Lucida Console","Bitstream Vera Sans Mono","Courier",monospace !important;
- }
- a:link {
- text-decoration: none;
- }
- div.text {
- padding: 5px;
- }
- </style>
-
- </head>
- <body>
- <center>
- <div class="icon_container">
- <div class="icon">&#x23c1;</div>
- </div>
- </center>
-
- <div class="text">
- <h2>Getting Started</h2>
-
- <p>Getting started is simple. Simply click <font class="code">Join Network</font> from the ZeroTier status bar menu. To join the public network "Earth", enter <font class="code">8056c2e21c000001</font> and click the Join button. Once connected, you'll be able to navigate to <a href="http://earth.zerotier.net">earth.zerotier.net</a>.</p>
-
- <h3>Create a Network</h3>
- <p>Visit <a href="http://my.zerotier.com">my.zerotier.com</a> to create and manage your own virtual networks.</p>
-
- <p>For more information, visit <a href="http://www.zerotier.com">zerotier.com</a>.</p>
-
- </div>
- </body>
+<head>
+<style type="text/css">
+html,body {
+ background: #ffffff;
+ margin: 0;
+ padding: 0;
+ font-family: sans-serif;
+ font-size: 12pt;
+ height: 100%;
+ width: 100%;
+}
+div.icon {
+ background: #ffb354;
+ color: #000000;
+ font-size: 100pt;
+ border-radius: 2.5rem;
+ display: inline-block;
+ width: 1.3em;
+ height: 1.3em;
+ padding: 0;
+ margin: 0;
+ line-height: 1.4em;
+ vertical-align: middle;
+ text-align: center;
+}
+div.icon_container {
+ font-weight: bold;
+}
+a,p,h1,h2,h3,h4,span,div,strong,center,lead,nav,ol,ul,li,img,button,input,textarea,form {
+ font-family: sans-serif;
+}
+.code {
+ font-family: "Menlo",monospace !important;
+}
+a:link {
+ text-decoration: none;
+}
+</style>
+</head>
+<body>
+ <center><div class="icon_container"><div class="icon">&#x23c1;</div></div></center>
+ <div>
+ <h2>Welcome to ZeroTier</h2>
+ <h4>Getting Started</h4>
+ <p>Networks are identified by 16-digit network IDs. If someone invited you to join theirs you probably received one. If not you can create your own at <a href="https://my.zerotier.com/">my.zerotier.com</a> or by running <a href="https://github.com/zerotier/ZeroTierOne/tree/master/controller">running your own network controller</a>.
+ <p>Your computer is identified by a 10-digit ZeroTier address. You can find it at the top of the ZeroTier app's pull-down menu or by typing <span class="code">"sudo zerotier-cli info"</span> in a terminal window. This number is unique to your system and is how network administrators can recognize you. If someone invited you to a network, give them this ID so they can authorize you to join.</p>
+ <h4>Starting, Stopping, and Uninstalling</h4>
+ <p>The ZeroTier service is separate from the UI app and starts on system boot. The app can be started on login or only when needed. To stop the ZeroTier service use:<br><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="code">sudo launchctl unload /Library/LaunchDaemons/com.zerotier.one.plist</span><br><br>
+ Replace "unload" with "load" to start it again.</p>
+ <p>ZeroTier can be uninstalled with:<br><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="code">sudo '/Library/Application Support/ZeroTier/One/uninstall.sh'
+ </p>
+ <p><b>For more information, visit <a href="http://www.zerotier.com">zerotier.com</a>.</b></p>
+ </div>
+</body>
</html> \ No newline at end of file
diff --git a/make-bsd.mk b/make-bsd.mk
index 800c0c2a..713cafc0 100644
--- a/make-bsd.mk
+++ b/make-bsd.mk
@@ -5,19 +5,18 @@ DEFS=
LIBS=
include objects.mk
-OBJS+=osdep/BSDEthernetTap.o ext/http-parser/http_parser.o
+ONE_OBJS+=osdep/BSDEthernetTap.o ext/http-parser/http_parser.o
-# Build with ZT_ENABLE_CLUSTER=1 to build with cluster support
-ifeq ($(ZT_ENABLE_CLUSTER),1)
- DEFS+=-DZT_ENABLE_CLUSTER
+# Build with address sanitization library for advanced debugging (clang)
+ifeq ($(ZT_SANITIZE),1)
+ SANFLAGS+=-fsanitize=address -DASAN_OPTIONS=symbolize=1
endif
-
# "make debug" is a shortcut for this
ifeq ($(ZT_DEBUG),1)
- DEFS+=-DZT_TRACE
- CFLAGS+=-Wall -g -pthread $(INCLUDES) $(DEFS)
+ CFLAGS+=-Wall -Werror -g -pthread $(INCLUDES) $(DEFS)
LDFLAGS+=
STRIP=echo
+ ZT_TRACE=1
# The following line enables optimization for the crypto code, since
# C25519 in particular is almost UNUSABLE in heavy testing without it.
node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CFLAGS = -Wall -O2 -g -pthread $(INCLUDES) $(DEFS)
@@ -28,80 +27,84 @@ else
STRIP=strip --strip-all
endif
+ifeq ($(ZT_TRACE),1)
+ DEFS+=-DZT_TRACE
+endif
+
# Determine system build architecture from compiler target
CC_MACH=$(shell $(CC) -dumpmachine | cut -d '-' -f 1)
ZT_ARCHITECTURE=999
ifeq ($(CC_MACH),x86_64)
- ZT_ARCHITECTURE=2
+ ZT_ARCHITECTURE=2
ZT_USE_X64_ASM_SALSA2012=1
endif
ifeq ($(CC_MACH),amd64)
- ZT_ARCHITECTURE=2
+ ZT_ARCHITECTURE=2
ZT_USE_X64_ASM_SALSA2012=1
endif
ifeq ($(CC_MACH),i386)
- ZT_ARCHITECTURE=1
+ ZT_ARCHITECTURE=1
endif
ifeq ($(CC_MACH),i686)
- ZT_ARCHITECTURE=1
+ ZT_ARCHITECTURE=1
endif
ifeq ($(CC_MACH),arm)
- ZT_ARCHITECTURE=3
+ ZT_ARCHITECTURE=3
override DEFS+=-DZT_NO_TYPE_PUNNING
ZT_USE_ARM32_NEON_ASM_SALSA2012=1
endif
ifeq ($(CC_MACH),armel)
- ZT_ARCHITECTURE=3
+ ZT_ARCHITECTURE=3
override DEFS+=-DZT_NO_TYPE_PUNNING
ZT_USE_ARM32_NEON_ASM_SALSA2012=1
endif
ifeq ($(CC_MACH),armhf)
- ZT_ARCHITECTURE=3
+ ZT_ARCHITECTURE=3
override DEFS+=-DZT_NO_TYPE_PUNNING
ZT_USE_ARM32_NEON_ASM_SALSA2012=1
endif
ifeq ($(CC_MACH),armv6)
- ZT_ARCHITECTURE=3
+ ZT_ARCHITECTURE=3
override DEFS+=-DZT_NO_TYPE_PUNNING
ZT_USE_ARM32_NEON_ASM_SALSA2012=1
endif
ifeq ($(CC_MACH),armv6zk)
- ZT_ARCHITECTURE=3
+ ZT_ARCHITECTURE=3
override DEFS+=-DZT_NO_TYPE_PUNNING
ZT_USE_ARM32_NEON_ASM_SALSA2012=1
endif
ifeq ($(CC_MACH),armv6kz)
- ZT_ARCHITECTURE=3
+ ZT_ARCHITECTURE=3
override DEFS+=-DZT_NO_TYPE_PUNNING
ZT_USE_ARM32_NEON_ASM_SALSA2012=1
endif
ifeq ($(CC_MACH),armv7)
- ZT_ARCHITECTURE=3
+ ZT_ARCHITECTURE=3
override DEFS+=-DZT_NO_TYPE_PUNNING
ZT_USE_ARM32_NEON_ASM_SALSA2012=1
endif
ifeq ($(CC_MACH),arm64)
- ZT_ARCHITECTURE=4
+ ZT_ARCHITECTURE=4
override DEFS+=-DZT_NO_TYPE_PUNNING
endif
ifeq ($(CC_MACH),aarch64)
- ZT_ARCHITECTURE=4
+ ZT_ARCHITECTURE=4
override DEFS+=-DZT_NO_TYPE_PUNNING
endif
ifeq ($(CC_MACH),mipsel)
- ZT_ARCHITECTURE=5
+ ZT_ARCHITECTURE=5
override DEFS+=-DZT_NO_TYPE_PUNNING
endif
ifeq ($(CC_MACH),mips)
- ZT_ARCHITECTURE=5
+ ZT_ARCHITECTURE=5
override DEFS+=-DZT_NO_TYPE_PUNNING
endif
ifeq ($(CC_MACH),mips64)
- ZT_ARCHITECTURE=6
+ ZT_ARCHITECTURE=6
override DEFS+=-DZT_NO_TYPE_PUNNING
endif
ifeq ($(CC_MACH),mips64el)
- ZT_ARCHITECTURE=6
+ ZT_ARCHITECTURE=6
override DEFS+=-DZT_NO_TYPE_PUNNING
endif
@@ -115,11 +118,11 @@ endif
# Build faster crypto on some targets
ifeq ($(ZT_USE_X64_ASM_SALSA2012),1)
override DEFS+=-DZT_USE_X64_ASM_SALSA2012
- override OBJS+=ext/x64-salsa2012-asm/salsa2012.o
+ override CORE_OBJS+=ext/x64-salsa2012-asm/salsa2012.o
endif
ifeq ($(ZT_USE_ARM32_NEON_ASM_SALSA2012),1)
override DEFS+=-DZT_USE_ARM32_NEON_ASM_SALSA2012
- override OBJS+=ext/arm32-neon-salsa2012-asm/salsa2012.o
+ override CORE_OBJS+=ext/arm32-neon-salsa2012-asm/salsa2012.o
endif
override DEFS+=-DZT_BUILD_PLATFORM=$(ZT_BUILD_PLATFORM) -DZT_BUILD_ARCHITECTURE=$(ZT_ARCHITECTURE) -DZT_SOFTWARE_UPDATE_DEFAULT="\"disable\""
@@ -128,18 +131,32 @@ CXXFLAGS+=$(CFLAGS) -fno-rtti -std=c++11 #-D_GLIBCXX_USE_C99 -D_GLIBCXX_USE_C99_
all: one
-one: $(OBJS) service/OneService.o one.o
- $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-one $(OBJS) service/OneService.o one.o $(LIBS)
+one: $(CORE_OBJS) $(ONE_OBJS) one.o
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-one $(CORE_OBJS) $(ONE_OBJS) one.o $(LIBS)
$(STRIP) zerotier-one
ln -sf zerotier-one zerotier-idtool
ln -sf zerotier-one zerotier-cli
-selftest: $(OBJS) selftest.o
- $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LIBS)
+zerotier-one: one
+
+zerotier-idtool: one
+
+zerotier-cli: one
+
+libzerotiercore.a: $(CORE_OBJS)
+ ar rcs libzerotiercore.a $(CORE_OBJS)
+ ranlib libzerotiercore.a
+
+core: libzerotiercore.a
+
+selftest: $(CORE_OBJS) $(ONE_OBJS) selftest.o
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-selftest selftest.o $(CORE_OBJS) $(ONE_OBJS) $(LIBS)
$(STRIP) zerotier-selftest
+zerotier-selftest: selftest
+
clean:
- rm -rf *.o node/*.o controller/*.o osdep/*.o service/*.o ext/http-parser/*.o build-* zerotier-one zerotier-idtool zerotier-selftest zerotier-cli ZeroTierOneInstaller-* $(OBJS)
+ rm -rf *.a *.o node/*.o controller/*.o osdep/*.o service/*.o ext/http-parser/*.o build-* zerotier-one zerotier-idtool zerotier-selftest zerotier-cli $(ONE_OBJS) $(CORE_OBJS)
debug: FORCE
gmake -j 4 ZT_DEBUG=1
diff --git a/make-linux.mk b/make-linux.mk
index 87d29afe..cd946491 100644
--- a/make-linux.mk
+++ b/make-linux.mk
@@ -1,77 +1,91 @@
# Automagically pick clang or gcc, with preference for clang
# This is only done if we have not overridden these with an environment or CLI variable
ifeq ($(origin CC),default)
- CC=$(shell if [ -e /usr/bin/clang ]; then echo clang; else echo gcc; fi)
+ CC:=$(shell if [ -e /usr/bin/clang ]; then echo clang; else echo gcc; fi)
+ CC:=$(shell if [ -e /opt/intel/bin/icc ]; then echo /opt/intel/bin/icc -ipo -ansi-alias; else echo $(CC); fi)
endif
ifeq ($(origin CXX),default)
- CXX=$(shell if [ -e /usr/bin/clang++ ]; then echo clang++; else echo g++; fi)
+ CXX:=$(shell if [ -e /usr/bin/clang++ ]; then echo clang++; else echo g++; fi)
+ CXX:=$(shell if [ -e /opt/intel/bin/icc ]; then echo /opt/intel/bin/icc -ipo -ansi-alias; else echo $(CXX); fi)
endif
INCLUDES?=
-DEFS?=-D_FORTIFY_SOURCE=2
+DEFS?=
LDLIBS?=
DESTDIR?=
include objects.mk
-
-# Use bundled http-parser since distribution versions are NOT API-stable or compatible!
-# Trying to use dynamically linked libhttp-parser causes tons of compatibility problems.
-OBJS+=ext/http-parser/http_parser.o
+ONE_OBJS+=osdep/LinuxEthernetTap.o
# Auto-detect miniupnpc and nat-pmp as well and use system libs if present,
# otherwise build into binary as done on Mac and Windows.
-OBJS+=osdep/PortMapper.o
-DEFS+=-DZT_USE_MINIUPNPC
-MINIUPNPC_IS_NEW_ENOUGH=$(shell grep -sqr '.*define.*MINIUPNPC_VERSION.*"2.."' /usr/include/miniupnpc/miniupnpc.h && echo 1)
+ONE_OBJS+=osdep/PortMapper.o
+override DEFS+=-DZT_USE_MINIUPNPC
+MINIUPNPC_IS_NEW_ENOUGH=$(shell grep -sqr '.*define.*MINIUPNPC_VERSION.*"2..*"' /usr/include/miniupnpc/miniupnpc.h && echo 1)
+#MINIUPNPC_IS_NEW_ENOUGH=$(shell grep -sqr '.*define.*MINIUPNPC_VERSION.*"2.."' /usr/include/miniupnpc/miniupnpc.h && echo 1)
ifeq ($(MINIUPNPC_IS_NEW_ENOUGH),1)
- DEFS+=-DZT_USE_SYSTEM_MINIUPNPC
+ override DEFS+=-DZT_USE_SYSTEM_MINIUPNPC
LDLIBS+=-lminiupnpc
else
- DEFS+=-DMINIUPNP_STATICLIB -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DOS_STRING=\"Linux\" -DMINIUPNPC_VERSION_STRING=\"2.0\" -DUPNP_VERSION_STRING=\"UPnP/1.1\" -DENABLE_STRNATPMPERR
- OBJS+=ext/miniupnpc/connecthostport.o ext/miniupnpc/igd_desc_parse.o ext/miniupnpc/minisoap.o ext/miniupnpc/minissdpc.o ext/miniupnpc/miniupnpc.o ext/miniupnpc/miniwget.o ext/miniupnpc/minixml.o ext/miniupnpc/portlistingparse.o ext/miniupnpc/receivedata.o ext/miniupnpc/upnpcommands.o ext/miniupnpc/upnpdev.o ext/miniupnpc/upnperrors.o ext/miniupnpc/upnpreplyparse.o
+ override DEFS+=-DMINIUPNP_STATICLIB -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DOS_STRING=\"Linux\" -DMINIUPNPC_VERSION_STRING=\"2.0\" -DUPNP_VERSION_STRING=\"UPnP/1.1\" -DENABLE_STRNATPMPERR
+ ONE_OBJS+=ext/miniupnpc/connecthostport.o ext/miniupnpc/igd_desc_parse.o ext/miniupnpc/minisoap.o ext/miniupnpc/minissdpc.o ext/miniupnpc/miniupnpc.o ext/miniupnpc/miniwget.o ext/miniupnpc/minixml.o ext/miniupnpc/portlistingparse.o ext/miniupnpc/receivedata.o ext/miniupnpc/upnpcommands.o ext/miniupnpc/upnpdev.o ext/miniupnpc/upnperrors.o ext/miniupnpc/upnpreplyparse.o
endif
ifeq ($(wildcard /usr/include/natpmp.h),)
- OBJS+=ext/libnatpmp/natpmp.o ext/libnatpmp/getgateway.o
+ ONE_OBJS+=ext/libnatpmp/natpmp.o ext/libnatpmp/getgateway.o
else
LDLIBS+=-lnatpmp
- DEFS+=-DZT_USE_SYSTEM_NATPMP
+ override DEFS+=-DZT_USE_SYSTEM_NATPMP
endif
-ifeq ($(ZT_ENABLE_CLUSTER),1)
- DEFS+=-DZT_ENABLE_CLUSTER
-endif
+# Use bundled http-parser since distribution versions are NOT API-stable or compatible!
+# Trying to use dynamically linked libhttp-parser causes tons of compatibility problems.
+ONE_OBJS+=ext/http-parser/http_parser.o
ifeq ($(ZT_SYNOLOGY), 1)
- DEFS+=-D__SYNOLOGY__
+ override DEFS+=-D__SYNOLOGY__
+endif
+
+ifeq ($(ZT_QNAP), 1)
+ override DEFS+=-D__QNAP__
endif
ifeq ($(ZT_TRACE),1)
- DEFS+=-DZT_TRACE
+ override DEFS+=-DZT_TRACE
endif
ifeq ($(ZT_RULES_ENGINE_DEBUGGING),1)
- DEFS+=-DZT_RULES_ENGINE_DEBUGGING
+ override DEFS+=-DZT_RULES_ENGINE_DEBUGGING
endif
+# Build with address sanitization library for advanced debugging (clang)
+ifeq ($(ZT_SANITIZE),1)
+ SANFLAGS+=-fsanitize=address -DASAN_OPTIONS=symbolize=1
+endif
ifeq ($(ZT_DEBUG),1)
- DEFS+=-DZT_TRACE
- override CFLAGS+=-Wall -g -O -pthread $(INCLUDES) $(DEFS)
- override CXXFLAGS+=-Wall -g -O -std=c++11 -pthread $(INCLUDES) $(DEFS)
- override LDFLAGS+=
+ override CFLAGS+=-Wall -Wno-deprecated -Werror -g -pthread $(INCLUDES) $(DEFS)
+ override CXXFLAGS+=-Wall -Wno-deprecated -Werror -g -std=c++11 -pthread $(INCLUDES) $(DEFS)
+ ZT_TRACE=1
STRIP?=echo
# The following line enables optimization for the crypto code, since
# C25519 in particular is almost UNUSABLE in -O0 even on a 3ghz box!
-node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CFLAGS = -Wall -O2 -g -pthread $(INCLUDES) $(DEFS)
+node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CXXFLAGS=-Wall -O2 -g -pthread $(INCLUDES) $(DEFS)
else
CFLAGS?=-O3 -fstack-protector
- override CFLAGS+=-Wall -fPIE -pthread $(INCLUDES) -DNDEBUG $(DEFS)
+ override CFLAGS+=-Wall -Wno-deprecated -pthread $(INCLUDES) -DNDEBUG $(DEFS)
CXXFLAGS?=-O3 -fstack-protector
- override CXXFLAGS+=-Wall -Wno-unused-result -Wreorder -fPIE -std=c++11 -pthread $(INCLUDES) -DNDEBUG $(DEFS)
- override LDFLAGS+=-pie -Wl,-z,relro,-z,now
+ override CXXFLAGS+=-Wall -Wno-deprecated -std=c++11 -pthread $(INCLUDES) -DNDEBUG $(DEFS)
STRIP?=strip
STRIP+=--strip-all
endif
+ifeq ($(ZT_TRACE),1)
+ override DEFS+=-DZT_TRACE
+endif
+
+ifeq ($(ZT_USE_TEST_TAP),1)
+ override DEFS+=-DZT_USE_TEST_TAP
+endif
+
# Uncomment for gprof profile build
#CFLAGS=-Wall -g -pg -pthread $(INCLUDES) $(DEFS)
#CXXFLAGS=-Wall -g -pg -pthread $(INCLUDES) $(DEFS)
@@ -82,76 +96,100 @@ endif
CC_MACH=$(shell $(CC) -dumpmachine | cut -d '-' -f 1)
ZT_ARCHITECTURE=999
ifeq ($(CC_MACH),x86_64)
- ZT_ARCHITECTURE=2
- ZT_USE_X64_ASM_SALSA2012=1
+ ZT_ARCHITECTURE=2
+ ZT_USE_X64_ASM_CRYPTO=1
endif
ifeq ($(CC_MACH),amd64)
- ZT_ARCHITECTURE=2
- ZT_USE_X64_ASM_SALSA2012=1
+ ZT_ARCHITECTURE=2
+ ZT_USE_X64_ASM_CRYPTO=1
+endif
+ifeq ($(CC_MACH),powerpc64le)
+ ZT_ARCHITECTURE=8
+endif
+ifeq ($(CC_MACH),ppc64le)
+ ZT_ARCHITECTURE=8
+endif
+ifeq ($(CC_MACH),ppc64el)
+ ZT_ARCHITECTURE=8
endif
ifeq ($(CC_MACH),i386)
- ZT_ARCHITECTURE=1
+ ZT_ARCHITECTURE=1
+endif
+ifeq ($(CC_MACH),i486)
+ ZT_ARCHITECTURE=1
+endif
+ifeq ($(CC_MACH),i586)
+ ZT_ARCHITECTURE=1
endif
ifeq ($(CC_MACH),i686)
- ZT_ARCHITECTURE=1
+ ZT_ARCHITECTURE=1
endif
ifeq ($(CC_MACH),arm)
- ZT_ARCHITECTURE=3
+ ZT_ARCHITECTURE=3
override DEFS+=-DZT_NO_TYPE_PUNNING
- ZT_USE_ARM32_NEON_ASM_SALSA2012=1
+ ZT_USE_ARM32_NEON_ASM_CRYPTO=1
endif
ifeq ($(CC_MACH),armel)
- ZT_ARCHITECTURE=3
+ ZT_ARCHITECTURE=3
override DEFS+=-DZT_NO_TYPE_PUNNING
- ZT_USE_ARM32_NEON_ASM_SALSA2012=1
+ ZT_USE_ARM32_NEON_ASM_CRYPTO=1
endif
ifeq ($(CC_MACH),armhf)
- ZT_ARCHITECTURE=3
+ ZT_ARCHITECTURE=3
override DEFS+=-DZT_NO_TYPE_PUNNING
- ZT_USE_ARM32_NEON_ASM_SALSA2012=1
+ ZT_USE_ARM32_NEON_ASM_CRYPTO=1
endif
ifeq ($(CC_MACH),armv6)
- ZT_ARCHITECTURE=3
+ ZT_ARCHITECTURE=3
override DEFS+=-DZT_NO_TYPE_PUNNING
- ZT_USE_ARM32_NEON_ASM_SALSA2012=1
+ ZT_USE_ARM32_NEON_ASM_CRYPTO=1
endif
ifeq ($(CC_MACH),armv6zk)
- ZT_ARCHITECTURE=3
+ ZT_ARCHITECTURE=3
override DEFS+=-DZT_NO_TYPE_PUNNING
- ZT_USE_ARM32_NEON_ASM_SALSA2012=1
+ ZT_USE_ARM32_NEON_ASM_CRYPTO=1
endif
ifeq ($(CC_MACH),armv6kz)
- ZT_ARCHITECTURE=3
+ ZT_ARCHITECTURE=3
override DEFS+=-DZT_NO_TYPE_PUNNING
- ZT_USE_ARM32_NEON_ASM_SALSA2012=1
+ ZT_USE_ARM32_NEON_ASM_CRYPTO=1
endif
ifeq ($(CC_MACH),armv7)
- ZT_ARCHITECTURE=3
+ ZT_ARCHITECTURE=3
override DEFS+=-DZT_NO_TYPE_PUNNING
- ZT_USE_ARM32_NEON_ASM_SALSA2012=1
+ ZT_USE_ARM32_NEON_ASM_CRYPTO=1
+endif
+ifeq ($(CC_MACH),armv7l)
+ ZT_ARCHITECTURE=3
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+ ZT_USE_ARM32_NEON_ASM_CRYPTO=1
endif
ifeq ($(CC_MACH),arm64)
- ZT_ARCHITECTURE=4
+ ZT_ARCHITECTURE=4
override DEFS+=-DZT_NO_TYPE_PUNNING
endif
ifeq ($(CC_MACH),aarch64)
- ZT_ARCHITECTURE=4
+ ZT_ARCHITECTURE=4
override DEFS+=-DZT_NO_TYPE_PUNNING
endif
ifeq ($(CC_MACH),mipsel)
- ZT_ARCHITECTURE=5
+ ZT_ARCHITECTURE=5
override DEFS+=-DZT_NO_TYPE_PUNNING
endif
ifeq ($(CC_MACH),mips)
- ZT_ARCHITECTURE=5
+ ZT_ARCHITECTURE=5
override DEFS+=-DZT_NO_TYPE_PUNNING
endif
ifeq ($(CC_MACH),mips64)
- ZT_ARCHITECTURE=6
+ ZT_ARCHITECTURE=6
override DEFS+=-DZT_NO_TYPE_PUNNING
endif
ifeq ($(CC_MACH),mips64el)
- ZT_ARCHITECTURE=6
+ ZT_ARCHITECTURE=6
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+endif
+ifeq ($(CC_MACH),powerpc64le)
+ ZT_ARCHITECTURE=7
override DEFS+=-DZT_NO_TYPE_PUNNING
endif
@@ -165,54 +203,88 @@ endif
# Disable software updates by default on Linux since that is normally done with package management
override DEFS+=-DZT_BUILD_PLATFORM=1 -DZT_BUILD_ARCHITECTURE=$(ZT_ARCHITECTURE) -DZT_SOFTWARE_UPDATE_DEFAULT="\"disable\""
-# Build faster crypto on some targets
-ifeq ($(ZT_USE_X64_ASM_SALSA2012),1)
- override DEFS+=-DZT_USE_X64_ASM_SALSA2012
- override OBJS+=ext/x64-salsa2012-asm/salsa2012.o
-endif
-ifeq ($(ZT_USE_ARM32_NEON_ASM_SALSA2012),1)
- override DEFS+=-DZT_USE_ARM32_NEON_ASM_SALSA2012
- override OBJS+=ext/arm32-neon-salsa2012-asm/salsa2012.o
-endif
+# This forces libstdc++ not to include these abominations, especially mt and pool
+override DEFS+=-D_MT_ALLOCATOR_H -D_POOL_ALLOCATOR_H -D_EXTPTR_ALLOCATOR_H -D_DEBUG_ALLOCATOR_H
# Static builds, which are currently done for a number of Linux targets
ifeq ($(ZT_STATIC),1)
override LDFLAGS+=-static
- ifeq ($(ZT_ARCHITECTURE),3)
- ifeq ($(ZT_ARM_SOFTFLOAT),1)
- override CFLAGS+=-march=armv5te -mfloat-abi=soft -msoft-float -mno-unaligned-access -marm
- override CXXFLAGS+=-march=armv5te -mfloat-abi=soft -msoft-float -mno-unaligned-access -marm
- else
- override CFLAGS+=-march=armv6kz -mcpu=arm1176jzf-s -mfpu=vfp -mfloat-abi=hard -mno-unaligned-access -marm
- override CXXFLAGS+=-march=armv6kz -mcpu=arm1176jzf-s -mfpu=vfp -mfloat-abi=hard -mno-unaligned-access -marm
- endif
+endif
+
+# For building an official semi-static binary on CentOS 7
+ifeq ($(ZT_OFFICIAL),1)
+ CORE_OBJS+=ext/misc/linux-old-glibc-compat.o
+ override LDFLAGS+=-Wl,--wrap=memcpy -static-libstdc++
+endif
+
+# ARM32 hell -- use conservative CFLAGS
+ifeq ($(ZT_ARCHITECTURE),3)
+ ifeq ($(shell if [ -e /usr/bin/dpkg ]; then dpkg --print-architecture; fi),armel)
+ override CFLAGS+=-march=armv5 -mfloat-abi=soft -msoft-float -mno-unaligned-access -marm
+ override CXXFLAGS+=-march=armv5 -mfloat-abi=soft -msoft-float -mno-unaligned-access -marm
+ ZT_USE_ARM32_NEON_ASM_CRYPTO=0
+ else
+ override CFLAGS+=-march=armv5 -mno-unaligned-access -marm
+ override CXXFLAGS+=-march=armv5 -mno-unaligned-access -marm
endif
endif
+# Build faster crypto on some targets
+ifeq ($(ZT_USE_X64_ASM_CRYPTO),1)
+ override DEFS+=-DZT_USE_X64_ASM_SALSA2012 -DZT_USE_FAST_X64_ED25519
+ override CORE_OBJS+=ext/x64-salsa2012-asm/salsa2012.o ext/ed25519-amd64-asm/choose_t.o ext/ed25519-amd64-asm/consts.o ext/ed25519-amd64-asm/fe25519_add.o ext/ed25519-amd64-asm/fe25519_freeze.o ext/ed25519-amd64-asm/fe25519_mul.o ext/ed25519-amd64-asm/fe25519_square.o ext/ed25519-amd64-asm/fe25519_sub.o ext/ed25519-amd64-asm/ge25519_add_p1p1.o ext/ed25519-amd64-asm/ge25519_dbl_p1p1.o ext/ed25519-amd64-asm/ge25519_nielsadd2.o ext/ed25519-amd64-asm/ge25519_nielsadd_p1p1.o ext/ed25519-amd64-asm/ge25519_p1p1_to_p2.o ext/ed25519-amd64-asm/ge25519_p1p1_to_p3.o ext/ed25519-amd64-asm/ge25519_pnielsadd_p1p1.o ext/ed25519-amd64-asm/heap_rootreplaced.o ext/ed25519-amd64-asm/heap_rootreplaced_1limb.o ext/ed25519-amd64-asm/heap_rootreplaced_2limbs.o ext/ed25519-amd64-asm/heap_rootreplaced_3limbs.o ext/ed25519-amd64-asm/sc25519_add.o ext/ed25519-amd64-asm/sc25519_barrett.o ext/ed25519-amd64-asm/sc25519_lt.o ext/ed25519-amd64-asm/sc25519_sub_nored.o ext/ed25519-amd64-asm/ull4_mul.o ext/ed25519-amd64-asm/fe25519_getparity.o ext/ed25519-amd64-asm/fe25519_invert.o ext/ed25519-amd64-asm/fe25519_iseq.o ext/ed25519-amd64-asm/fe25519_iszero.o ext/ed25519-amd64-asm/fe25519_neg.o ext/ed25519-amd64-asm/fe25519_pack.o ext/ed25519-amd64-asm/fe25519_pow2523.o ext/ed25519-amd64-asm/fe25519_setint.o ext/ed25519-amd64-asm/fe25519_unpack.o ext/ed25519-amd64-asm/ge25519_add.o ext/ed25519-amd64-asm/ge25519_base.o ext/ed25519-amd64-asm/ge25519_double.o ext/ed25519-amd64-asm/ge25519_double_scalarmult.o ext/ed25519-amd64-asm/ge25519_isneutral.o ext/ed25519-amd64-asm/ge25519_multi_scalarmult.o ext/ed25519-amd64-asm/ge25519_pack.o ext/ed25519-amd64-asm/ge25519_scalarmult_base.o ext/ed25519-amd64-asm/ge25519_unpackneg.o ext/ed25519-amd64-asm/hram.o ext/ed25519-amd64-asm/index_heap.o ext/ed25519-amd64-asm/sc25519_from32bytes.o ext/ed25519-amd64-asm/sc25519_from64bytes.o ext/ed25519-amd64-asm/sc25519_from_shortsc.o ext/ed25519-amd64-asm/sc25519_iszero.o ext/ed25519-amd64-asm/sc25519_mul.o ext/ed25519-amd64-asm/sc25519_mul_shortsc.o ext/ed25519-amd64-asm/sc25519_slide.o ext/ed25519-amd64-asm/sc25519_to32bytes.o ext/ed25519-amd64-asm/sc25519_window4.o ext/ed25519-amd64-asm/sign.o
+endif
+ifeq ($(ZT_USE_ARM32_NEON_ASM_CRYPTO),1)
+ override DEFS+=-DZT_USE_ARM32_NEON_ASM_SALSA2012
+ override CORE_OBJS+=ext/arm32-neon-salsa2012-asm/salsa2012.o
+endif
+
all: one
-one: $(OBJS) service/OneService.o one.o osdep/LinuxEthernetTap.o
- $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-one $(OBJS) service/OneService.o one.o osdep/LinuxEthernetTap.o $(LDLIBS)
+one: $(CORE_OBJS) $(ONE_OBJS) one.o
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-one $(CORE_OBJS) $(ONE_OBJS) one.o $(LDLIBS)
$(STRIP) zerotier-one
ln -sf zerotier-one zerotier-idtool
ln -sf zerotier-one zerotier-cli
-selftest: $(OBJS) selftest.o
- $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LDLIBS)
+zerotier-one: one
+
+zerotier-idtool: one
+
+zerotier-cli: one
+
+libzerotiercore.a: FORCE
+ make CFLAGS="-O3 -fstack-protector -fPIC" CXXFLAGS="-O3 -std=c++11 -fstack-protector -fPIC" $(CORE_OBJS)
+ ar rcs libzerotiercore.a $(CORE_OBJS)
+ ranlib libzerotiercore.a
+
+core: libzerotiercore.a
+
+selftest: $(CORE_OBJS) $(ONE_OBJS) selftest.o
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-selftest selftest.o $(CORE_OBJS) $(ONE_OBJS) $(LDLIBS)
$(STRIP) zerotier-selftest
+zerotier-selftest: selftest
+
manpages: FORCE
cd doc ; ./build.sh
doc: manpages
clean: FORCE
- rm -rf *.so *.o node/*.o controller/*.o osdep/*.o service/*.o ext/http-parser/*.o ext/miniupnpc/*.o ext/libnatpmp/*.o $(OBJS) zerotier-one zerotier-idtool zerotier-cli zerotier-selftest build-* ZeroTierOneInstaller-* *.deb *.rpm .depend debian/files debian/zerotier-one*.debhelper debian/zerotier-one.substvars debian/*.log debian/zerotier-one doc/node_modules
+ rm -rf *.a *.so *.o node/*.o controller/*.o osdep/*.o service/*.o ext/http-parser/*.o ext/miniupnpc/*.o ext/libnatpmp/*.o $(CORE_OBJS) $(ONE_OBJS) zerotier-one zerotier-idtool zerotier-cli zerotier-selftest build-* ZeroTierOneInstaller-* *.deb *.rpm .depend debian/files debian/zerotier-one*.debhelper debian/zerotier-one.substvars debian/*.log debian/zerotier-one doc/node_modules ext/misc/*.o
distclean: clean
realclean: distclean
+official: FORCE
+ make -j4 ZT_OFFICIAL=1 all
+
+central-controller: FORCE
+ cd ext/librethinkdbxx ; make
+ make -j4 LDLIBS="ext/librethinkdbxx/build/librethinkdb++.a" DEFS="-DZT_CONTROLLER_USE_RETHINKDB" ZT_OFFICIAL=1 one
+
debug: FORCE
make ZT_DEBUG=1 one
make ZT_DEBUG=1 selftest
diff --git a/make-mac.mk b/make-mac.mk
index 6676f457..60aa465a 100644
--- a/make-mac.mk
+++ b/make-mac.mk
@@ -19,7 +19,7 @@ ZT_VERSION_BUILD=$(shell cat version.h | grep -F VERSION_BUILD | cut -d ' ' -f 3
DEFS+=-DZT_BUILD_PLATFORM=$(ZT_BUILD_PLATFORM) -DZT_BUILD_ARCHITECTURE=$(ZT_BUILD_ARCHITECTURE)
include objects.mk
-OBJS+=osdep/OSXEthernetTap.o ext/http-parser/http_parser.o
+ONE_OBJS+=osdep/OSXEthernetTap.o ext/http-parser/http_parser.o
# Official releases are signed with our Apple cert and apply software updates by default
ifeq ($(ZT_OFFICIAL_RELEASE),1)
@@ -33,32 +33,36 @@ else
DEFS+=-DZT_SOFTWARE_UPDATE_DEFAULT="\"download\""
endif
-ifeq ($(ZT_ENABLE_CLUSTER),1)
- DEFS+=-DZT_ENABLE_CLUSTER
-endif
-
# Use fast ASM Salsa20/12 for x64 processors
DEFS+=-DZT_USE_X64_ASM_SALSA2012
-OBJS+=ext/x64-salsa2012-asm/salsa2012.o
+CORE_OBJS+=ext/x64-salsa2012-asm/salsa2012.o
# Build miniupnpc and nat-pmp as included libraries -- extra defs are required for these sources
DEFS+=-DMACOSX -DZT_USE_MINIUPNPC -DMINIUPNP_STATICLIB -D_DARWIN_C_SOURCE -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -DOS_STRING=\"Darwin/15.0.0\" -DMINIUPNPC_VERSION_STRING=\"2.0\" -DUPNP_VERSION_STRING=\"UPnP/1.1\" -DENABLE_STRNATPMPERR
-OBJS+=ext/libnatpmp/natpmp.o ext/libnatpmp/getgateway.o ext/miniupnpc/connecthostport.o ext/miniupnpc/igd_desc_parse.o ext/miniupnpc/minisoap.o ext/miniupnpc/minissdpc.o ext/miniupnpc/miniupnpc.o ext/miniupnpc/miniwget.o ext/miniupnpc/minixml.o ext/miniupnpc/portlistingparse.o ext/miniupnpc/receivedata.o ext/miniupnpc/upnpcommands.o ext/miniupnpc/upnpdev.o ext/miniupnpc/upnperrors.o ext/miniupnpc/upnpreplyparse.o osdep/PortMapper.o
+ONE_OBJS+=ext/libnatpmp/natpmp.o ext/libnatpmp/getgateway.o ext/miniupnpc/connecthostport.o ext/miniupnpc/igd_desc_parse.o ext/miniupnpc/minisoap.o ext/miniupnpc/minissdpc.o ext/miniupnpc/miniupnpc.o ext/miniupnpc/miniwget.o ext/miniupnpc/minixml.o ext/miniupnpc/portlistingparse.o ext/miniupnpc/receivedata.o ext/miniupnpc/upnpcommands.o ext/miniupnpc/upnpdev.o ext/miniupnpc/upnperrors.o ext/miniupnpc/upnpreplyparse.o osdep/PortMapper.o
+# Build with address sanitization library for advanced debugging (clang)
+ifeq ($(ZT_SANITIZE),1)
+ SANFLAGS+=-fsanitize=address -DASAN_OPTIONS=symbolize=1
+endif
# Debug mode -- dump trace output, build binary with -g
ifeq ($(ZT_DEBUG),1)
- DEFS+=-DZT_TRACE
- CFLAGS+=-Wall -g -pthread $(INCLUDES) $(DEFS)
+ ZT_TRACE=1
+ CFLAGS+=-Wall -Werror -g $(INCLUDES) $(DEFS)
STRIP=echo
# The following line enables optimization for the crypto code, since
# C25519 in particular is almost UNUSABLE in heavy testing without it.
-node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CFLAGS = -Wall -O2 -g -pthread $(INCLUDES) $(DEFS)
+node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CFLAGS = -Wall -O2 -g $(INCLUDES) $(DEFS)
else
CFLAGS?=-Ofast -fstack-protector-strong
- CFLAGS+=$(ARCH_FLAGS) -Wall -flto -fPIE -pthread -mmacosx-version-min=10.7 -DNDEBUG -Wno-unused-private-field $(INCLUDES) $(DEFS)
+ CFLAGS+=$(ARCH_FLAGS) -Wall -Werror -flto -fPIE -mmacosx-version-min=10.7 -DNDEBUG -Wno-unused-private-field $(INCLUDES) $(DEFS)
STRIP=strip
endif
+ifeq ($(ZT_TRACE),1)
+ DEFS+=-DZT_TRACE
+endif
+
CXXFLAGS=$(CFLAGS) -std=c++11 -stdlib=libc++
all: one macui
@@ -66,13 +70,25 @@ all: one macui
ext/x64-salsa2012-asm/salsa2012.o:
$(CC) $(CFLAGS) -c ext/x64-salsa2012-asm/salsa2012.s -o ext/x64-salsa2012-asm/salsa2012.o
-one: $(OBJS) service/OneService.o one.o
- $(CXX) $(CXXFLAGS) -o zerotier-one $(OBJS) service/OneService.o one.o $(LIBS)
+one: $(CORE_OBJS) $(ONE_OBJS) one.o
+ $(CXX) $(CXXFLAGS) -o zerotier-one $(CORE_OBJS) $(ONE_OBJS) one.o $(LIBS)
$(STRIP) zerotier-one
ln -sf zerotier-one zerotier-idtool
ln -sf zerotier-one zerotier-cli
$(CODESIGN) -f -s $(CODESIGN_APP_CERT) zerotier-one
+zerotier-one: one
+
+zerotier-idtool: one
+
+zerotier-cli: one
+
+libzerotiercore.a: $(CORE_OBJS)
+ ar rcs libzerotiercore.a $(CORE_OBJS)
+ ranlib libzerotiercore.a
+
+core: libzerotiercore.a
+
macui: FORCE
cd macui && xcodebuild -target "ZeroTier One" -configuration Release
$(CODESIGN) -f -s $(CODESIGN_APP_CERT) "macui/build/Release/ZeroTier One.app"
@@ -81,10 +97,12 @@ macui: FORCE
# $(CXX) $(CXXFLAGS) -o zerotier cli/zerotier.cpp osdep/OSUtils.cpp node/InetAddress.cpp node/Utils.cpp node/Salsa20.cpp node/Identity.cpp node/SHA512.cpp node/C25519.cpp -lcurl
# $(STRIP) zerotier
-selftest: $(OBJS) selftest.o
- $(CXX) $(CXXFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LIBS)
+selftest: $(CORE_OBJS) $(ONE_OBJS) selftest.o
+ $(CXX) $(CXXFLAGS) -o zerotier-selftest selftest.o $(CORE_OBJS) $(ONE_OBJS) $(LIBS)
$(STRIP) zerotier-selftest
+zerotier-selftest: selftest
+
# Requires Packages: http://s.sudre.free.fr/Software/Packages/about.html
mac-dist-pkg: FORCE
packagesbuild "ext/installfiles/mac/ZeroTier One.pkgproj"
@@ -97,12 +115,12 @@ mac-dist-pkg: FORCE
# For ZeroTier, Inc. to build official signed packages
official: FORCE
make clean
- make ZT_OFFICIAL_RELEASE=1 -j 4 one
+ make ZT_OFFICIAL_RELEASE=1 -j 8 one
make ZT_OFFICIAL_RELEASE=1 macui
make ZT_OFFICIAL_RELEASE=1 mac-dist-pkg
clean:
- rm -rf *.dSYM build-* *.pkg *.dmg *.o node/*.o controller/*.o service/*.o osdep/*.o ext/http-parser/*.o $(OBJS) zerotier-one zerotier-idtool zerotier-selftest zerotier-cli zerotier mkworld doc/node_modules macui/build zt1_update_$(ZT_BUILD_PLATFORM)_$(ZT_BUILD_ARCHITECTURE)_*
+ rm -rf *.dSYM build-* *.a *.pkg *.dmg *.o node/*.o controller/*.o service/*.o osdep/*.o ext/http-parser/*.o $(CORE_OBJS) $(ONE_OBJS) zerotier-one zerotier-idtool zerotier-selftest zerotier-cli zerotier doc/node_modules macui/build zt1_update_$(ZT_BUILD_PLATFORM)_$(ZT_BUILD_ARCHITECTURE)_*
distclean: clean
diff --git a/node/Address.hpp b/node/Address.hpp
index 4a5883b0..41977af2 100644
--- a/node/Address.hpp
+++ b/node/Address.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_ADDRESS_HPP
@@ -133,20 +141,9 @@ public:
/**
* @return Hexadecimal string
*/
- inline std::string toString() const
- {
- char buf[16];
- Utils::snprintf(buf,sizeof(buf),"%.10llx",(unsigned long long)_a);
- return std::string(buf);
- };
-
- /**
- * @param buf Buffer to fill
- * @param len Length of buffer
- */
- inline void toString(char *buf,unsigned int len) const
+ inline char *toString(char buf[11]) const
{
- Utils::snprintf(buf,len,"%.10llx",(unsigned long long)_a);
+ return Utils::hex10(_a,buf);
}
/**
diff --git a/node/Array.hpp b/node/Array.hpp
deleted file mode 100644
index 19b29eb3..00000000
--- a/node/Array.hpp
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef ZT_ARRAY_HPP
-#define ZT_ARRAY_HPP
-
-#include <string>
-#include <algorithm>
-
-namespace ZeroTier {
-
-/**
- * Static array -- a simple thing that's belonged in STL since the time of the dinosaurs
- */
-template<typename T,std::size_t S>
-class Array
-{
-public:
- Array() throw() {}
-
- Array(const Array &a)
- {
- for(std::size_t i=0;i<S;++i)
- data[i] = a.data[i];
- }
-
- Array(const T *ptr)
- {
- for(std::size_t i=0;i<S;++i)
- data[i] = ptr[i];
- }
-
- inline Array &operator=(const Array &a)
- {
- for(std::size_t i=0;i<S;++i)
- data[i] = a.data[i];
- return *this;
- }
-
- typedef T value_type;
- typedef T* pointer;
- typedef const T* const_pointer;
- typedef T& reference;
- typedef const T& const_reference;
- typedef T* iterator;
- typedef const T* const_iterator;
- typedef std::size_t size_type;
- typedef std::ptrdiff_t difference_type;
- typedef std::reverse_iterator<iterator> reverse_iterator;
- typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
-
- inline iterator begin() throw() { return data; }
- inline iterator end() throw() { return &(data[S]); }
- inline const_iterator begin() const throw() { return data; }
- inline const_iterator end() const throw() { return &(data[S]); }
-
- inline reverse_iterator rbegin() throw() { return reverse_iterator(begin()); }
- inline reverse_iterator rend() throw() { return reverse_iterator(end()); }
- inline const_reverse_iterator rbegin() const throw() { return const_reverse_iterator(begin()); }
- inline const_reverse_iterator rend() const throw() { return const_reverse_iterator(end()); }
-
- inline std::size_t size() const throw() { return S; }
- inline std::size_t max_size() const throw() { return S; }
-
- inline reference operator[](const std::size_t n) throw() { return data[n]; }
- inline const_reference operator[](const std::size_t n) const throw() { return data[n]; }
-
- inline reference front() throw() { return data[0]; }
- inline const_reference front() const throw() { return data[0]; }
- inline reference back() throw() { return data[S-1]; }
- inline const_reference back() const throw() { return data[S-1]; }
-
- inline bool operator==(const Array &k) const throw()
- {
- for(unsigned long i=0;i<S;++i) {
- if (data[i] != k.data[i])
- return false;
- }
- return true;
- }
- inline bool operator<(const Array &k) const throw() { return std::lexicographical_compare(begin(),end(),k.begin(),k.end()); }
- inline bool operator!=(const Array &k) const throw() { return !(*this == k); }
- inline bool operator>(const Array &k) const throw() { return (k < *this); }
- inline bool operator<=(const Array &k) const throw() { return !(k < *this); }
- inline bool operator>=(const Array &k) const throw() { return !(*this < k); }
-
- T data[S];
-};
-
-} // namespace ZeroTier
-
-#endif
diff --git a/node/AtomicCounter.hpp b/node/AtomicCounter.hpp
index a0f29baa..a42a18d4 100644
--- a/node/AtomicCounter.hpp
+++ b/node/AtomicCounter.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,13 +14,20 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_ATOMICCOUNTER_HPP
#define ZT_ATOMICCOUNTER_HPP
#include "Constants.hpp"
-#include "NonCopyable.hpp"
#ifndef __GNUC__
#include <atomic>
@@ -31,12 +38,18 @@ namespace ZeroTier {
/**
* Simple atomic counter supporting increment and decrement
*/
-class AtomicCounter : NonCopyable
+class AtomicCounter
{
public:
- AtomicCounter()
+ AtomicCounter() { _v = 0; }
+
+ inline int load() const
{
- _v = 0;
+#ifdef __GNUC__
+ return __sync_or_and_fetch(const_cast<int *>(&_v),0);
+#else
+ return _v.load();
+#endif
}
inline int operator++()
@@ -58,6 +71,9 @@ public:
}
private:
+ AtomicCounter(const AtomicCounter &) {}
+ const AtomicCounter &operator=(const AtomicCounter &) { return *this; }
+
#ifdef __GNUC__
int _v;
#else
diff --git a/node/Buffer.hpp b/node/Buffer.hpp
index 37f39e7b..bbf4ee37 100644
--- a/node/Buffer.hpp
+++ b/node/Buffer.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_BUFFER_HPP
@@ -85,71 +93,55 @@ public:
}
Buffer(unsigned int l)
- throw(std::out_of_range)
{
if (l > C)
- throw std::out_of_range("Buffer: construct with size larger than capacity");
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
_l = l;
}
template<unsigned int C2>
Buffer(const Buffer<C2> &b)
- throw(std::out_of_range)
{
*this = b;
}
Buffer(const void *b,unsigned int l)
- throw(std::out_of_range)
{
copyFrom(b,l);
}
- Buffer(const std::string &s)
- throw(std::out_of_range)
- {
- copyFrom(s.data(),s.length());
- }
-
template<unsigned int C2>
inline Buffer &operator=(const Buffer<C2> &b)
- throw(std::out_of_range)
- {
- if (b._l > C)
- throw std::out_of_range("Buffer: assignment from buffer larger than capacity");
- memcpy(_b,b._b,_l = b._l);
- return *this;
- }
-
- inline Buffer &operator=(const std::string &s)
- throw(std::out_of_range)
{
- copyFrom(s.data(),s.length());
+ if (unlikely(b._l > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
+ if (C2 == C) {
+ ZT_FAST_MEMCPY(this,&b,sizeof(Buffer<C>));
+ } else {
+ ZT_FAST_MEMCPY(_b,b._b,_l = b._l);
+ }
return *this;
}
inline void copyFrom(const void *b,unsigned int l)
- throw(std::out_of_range)
{
- if (l > C)
- throw std::out_of_range("Buffer: set from C array larger than capacity");
+ if (unlikely(l > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
+ ZT_FAST_MEMCPY(_b,b,l);
_l = l;
- memcpy(_b,b,l);
}
unsigned char operator[](const unsigned int i) const
- throw(std::out_of_range)
{
- if (i >= _l)
- throw std::out_of_range("Buffer: [] beyond end of data");
+ if (unlikely(i >= _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
return (unsigned char)_b[i];
}
unsigned char &operator[](const unsigned int i)
- throw(std::out_of_range)
{
- if (i >= _l)
- throw std::out_of_range("Buffer: [] beyond end of data");
+ if (unlikely(i >= _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
return ((unsigned char *)_b)[i];
}
@@ -167,17 +159,15 @@ public:
* @throws std::out_of_range Field extends beyond data size
*/
unsigned char *field(unsigned int i,unsigned int l)
- throw(std::out_of_range)
{
- if ((i + l) > _l)
- throw std::out_of_range("Buffer: field() beyond end of data");
+ if (unlikely((i + l) > _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
return (unsigned char *)(_b + i);
}
const unsigned char *field(unsigned int i,unsigned int l) const
- throw(std::out_of_range)
{
- if ((i + l) > _l)
- throw std::out_of_range("Buffer: field() beyond end of data");
+ if (unlikely((i + l) > _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
return (const unsigned char *)(_b + i);
}
@@ -190,10 +180,9 @@ public:
*/
template<typename T>
inline void setAt(unsigned int i,const T v)
- throw(std::out_of_range)
{
- if ((i + sizeof(T)) > _l)
- throw std::out_of_range("Buffer: setAt() beyond end of data");
+ if (unlikely((i + sizeof(T)) > _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
#ifdef ZT_NO_TYPE_PUNNING
uint8_t *p = reinterpret_cast<uint8_t *>(_b + i);
for(unsigned int x=1;x<=sizeof(T);++x)
@@ -213,10 +202,9 @@ public:
*/
template<typename T>
inline T at(unsigned int i) const
- throw(std::out_of_range)
{
- if ((i + sizeof(T)) > _l)
- throw std::out_of_range("Buffer: at() beyond end of data");
+ if (unlikely((i + sizeof(T)) > _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
#ifdef ZT_NO_TYPE_PUNNING
T v = 0;
const uint8_t *p = reinterpret_cast<const uint8_t *>(_b + i);
@@ -240,10 +228,9 @@ public:
*/
template<typename T>
inline void append(const T v)
- throw(std::out_of_range)
{
- if ((_l + sizeof(T)) > C)
- throw std::out_of_range("Buffer: append beyond capacity");
+ if (unlikely((_l + sizeof(T)) > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
#ifdef ZT_NO_TYPE_PUNNING
uint8_t *p = reinterpret_cast<uint8_t *>(_b + _l);
for(unsigned int x=1;x<=sizeof(T);++x)
@@ -263,40 +250,39 @@ public:
* @throws std::out_of_range Attempt to append beyond capacity
*/
inline void append(unsigned char c,unsigned int n)
- throw(std::out_of_range)
{
- if ((_l + n) > C)
- throw std::out_of_range("Buffer: append beyond capacity");
+ if (unlikely((_l + n) > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
for(unsigned int i=0;i<n;++i)
_b[_l++] = (char)c;
}
/**
- * Append a C-array of bytes
+ * Append secure random bytes
*
- * @param b Data
- * @param l Length
- * @throws std::out_of_range Attempt to append beyond capacity
+ * @param n Number of random bytes to append
*/
- inline void append(const void *b,unsigned int l)
- throw(std::out_of_range)
+ inline void appendRandom(unsigned int n)
{
- if ((_l + l) > C)
- throw std::out_of_range("Buffer: append beyond capacity");
- memcpy(_b + _l,b,l);
- _l += l;
+ if (unlikely((_l + n) > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
+ Utils::getSecureRandom(_b + _l,n);
+ _l += n;
}
/**
- * Append a string
+ * Append a C-array of bytes
*
- * @param s String to append
+ * @param b Data
+ * @param l Length
* @throws std::out_of_range Attempt to append beyond capacity
*/
- inline void append(const std::string &s)
- throw(std::out_of_range)
+ inline void append(const void *b,unsigned int l)
{
- append(s.data(),(unsigned int)s.length());
+ if (unlikely((_l + l) > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
+ ZT_FAST_MEMCPY(_b + _l,b,l);
+ _l += l;
}
/**
@@ -306,11 +292,10 @@ public:
* @throws std::out_of_range Attempt to append beyond capacity
*/
inline void appendCString(const char *s)
- throw(std::out_of_range)
{
for(;;) {
- if (_l >= C)
- throw std::out_of_range("Buffer: append beyond capacity");
+ if (unlikely(_l >= C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
if (!(_b[_l++] = *(s++)))
break;
}
@@ -325,7 +310,6 @@ public:
*/
template<unsigned int C2>
inline void append(const Buffer<C2> &b)
- throw(std::out_of_range)
{
append(b._b,b._l);
}
@@ -341,10 +325,9 @@ public:
* @return Pointer to beginning of appended field of length 'l'
*/
inline char *appendField(unsigned int l)
- throw(std::out_of_range)
{
- if ((_l + l) > C)
- throw std::out_of_range("Buffer: append beyond capacity");
+ if (unlikely((_l + l) > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
char *r = _b + _l;
_l += l;
return r;
@@ -359,10 +342,9 @@ public:
* @throws std::out_of_range Capacity exceeded
*/
inline void addSize(unsigned int i)
- throw(std::out_of_range)
{
- if ((i + _l) > C)
- throw std::out_of_range("Buffer: setSize to larger than capacity");
+ if (unlikely((i + _l) > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
_l += i;
}
@@ -375,10 +357,9 @@ public:
* @throws std::out_of_range Size larger than capacity
*/
inline void setSize(const unsigned int i)
- throw(std::out_of_range)
{
- if (i > C)
- throw std::out_of_range("Buffer: setSize to larger than capacity");
+ if (unlikely(i > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
_l = i;
}
@@ -386,15 +367,14 @@ public:
* Move everything after 'at' to the buffer's front and truncate
*
* @param at Truncate before this position
- * @throw std::out_of_range Position is beyond size of buffer
+ * @throws std::out_of_range Position is beyond size of buffer
*/
inline void behead(const unsigned int at)
- throw(std::out_of_range)
{
if (!at)
return;
- if (at > _l)
- throw std::out_of_range("Buffer: behead() beyond capacity");
+ if (unlikely(at > _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
::memmove(_b,_b + at,_l -= at);
}
@@ -403,14 +383,13 @@ public:
*
* @param start Starting position
* @param length Length of block to erase
- * @throw std::out_of_range Position plus length is beyond size of buffer
+ * @throws std::out_of_range Position plus length is beyond size of buffer
*/
inline void erase(const unsigned int at,const unsigned int length)
- throw(std::out_of_range)
{
const unsigned int endr = at + length;
- if (endr > _l)
- throw std::out_of_range("Buffer: erase() range beyond end of buffer");
+ if (unlikely(endr > _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
::memmove(_b + at,_b + endr,_l - endr);
_l -= length;
}
@@ -487,8 +466,8 @@ public:
}
private:
- unsigned int _l;
char ZT_VAR_MAY_ALIAS _b[C];
+ unsigned int _l;
};
} // namespace ZeroTier
diff --git a/node/C25519.cpp b/node/C25519.cpp
index e9ffecc1..77084bd8 100644
--- a/node/C25519.cpp
+++ b/node/C25519.cpp
@@ -1,5 +1,3 @@
-// Code taken from NaCl by D. J. Bernstein and others
-
/*
Matthew Dempsky
Public domain.
@@ -7,7 +5,7 @@ Derived from public domain code by D. J. Bernstein.
*/
// Modified very slightly for ZeroTier One by Adam Ierymenko
-// (no functional changes)
+// This code remains in the public domain.
#include <stdint.h>
#include <stdlib.h>
@@ -22,7 +20,7 @@ Derived from public domain code by D. J. Bernstein.
#pragma warning(disable: 4146)
#endif
-namespace ZeroTier {
+namespace {
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
@@ -33,990 +31,853 @@ namespace ZeroTier {
#define crypto_uint64 uint64_t
#define crypto_hash_sha512_BYTES 64
-static inline void add(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])
+void add(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])
{
- unsigned int j;
- unsigned int u;
- u = 0;
- for (j = 0;j < 31;++j) { u += a[j] + b[j]; out[j] = u & 255; u >>= 8; }
- u += a[31] + b[31]; out[31] = u;
+ unsigned int j;
+ unsigned int u;
+ u = 0;
+ for (j = 0;j < 31;++j) { u += a[j] + b[j]; out[j] = u & 255; u >>= 8; }
+ u += a[31] + b[31]; out[31] = u;
}
-static inline void sub(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])
+void sub(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])
{
- unsigned int j;
- unsigned int u;
- u = 218;
- for (j = 0;j < 31;++j) {
- u += a[j] + 65280 - b[j];
- out[j] = u & 255;
- u >>= 8;
- }
- u += a[31] - b[31];
- out[31] = u;
+ unsigned int j;
+ unsigned int u;
+ u = 218;
+ for (j = 0;j < 31;++j) {
+ u += a[j] + 65280 - b[j];
+ out[j] = u & 255;
+ u >>= 8;
+ }
+ u += a[31] - b[31];
+ out[31] = u;
}
-static inline void squeeze(unsigned int a[32])
+void squeeze(unsigned int a[32])
{
- unsigned int j;
- unsigned int u;
- u = 0;
- for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; }
- u += a[31]; a[31] = u & 127;
- u = 19 * (u >> 7);
- for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; }
- u += a[31]; a[31] = u;
+ unsigned int j;
+ unsigned int u;
+ u = 0;
+ for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; }
+ u += a[31]; a[31] = u & 127;
+ u = 19 * (u >> 7);
+ for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; }
+ u += a[31]; a[31] = u;
}
static const unsigned int minusp[32] = {
19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128
} ;
-static inline void freeze(unsigned int a[32])
+void freeze(unsigned int a[32])
{
- unsigned int aorig[32];
- unsigned int j;
- unsigned int negative;
+ unsigned int aorig[32];
+ unsigned int j;
+ unsigned int negative;
- for (j = 0;j < 32;++j) aorig[j] = a[j];
- add(a,a,minusp);
- negative = -((a[31] >> 7) & 1);
- for (j = 0;j < 32;++j) a[j] ^= negative & (aorig[j] ^ a[j]);
+ for (j = 0;j < 32;++j) aorig[j] = a[j];
+ add(a,a,minusp);
+ negative = -((a[31] >> 7) & 1);
+ for (j = 0;j < 32;++j) a[j] ^= negative & (aorig[j] ^ a[j]);
}
-static inline void mult(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])
+void mult(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])
{
- unsigned int i;
- unsigned int j;
- unsigned int u;
+ unsigned int i;
+ unsigned int j;
+ unsigned int u;
- for (i = 0;i < 32;++i) {
- u = 0;
- for (j = 0;j <= i;++j) u += a[j] * b[i - j];
- for (j = i + 1;j < 32;++j) u += 38 * a[j] * b[i + 32 - j];
- out[i] = u;
- }
- squeeze(out);
+ for (i = 0;i < 32;++i) {
+ u = 0;
+ for (j = 0;j <= i;++j) u += a[j] * b[i - j];
+ for (j = i + 1;j < 32;++j) u += 38 * a[j] * b[i + 32 - j];
+ out[i] = u;
+ }
+ squeeze(out);
}
-static inline void mult121665(unsigned int out[32],const unsigned int a[32])
+void mult121665(unsigned int out[32],const unsigned int a[32])
{
- unsigned int j;
- unsigned int u;
+ unsigned int j;
+ unsigned int u;
- u = 0;
- for (j = 0;j < 31;++j) { u += 121665 * a[j]; out[j] = u & 255; u >>= 8; }
- u += 121665 * a[31]; out[31] = u & 127;
- u = 19 * (u >> 7);
- for (j = 0;j < 31;++j) { u += out[j]; out[j] = u & 255; u >>= 8; }
- u += out[j]; out[j] = u;
+ u = 0;
+ for (j = 0;j < 31;++j) { u += 121665 * a[j]; out[j] = u & 255; u >>= 8; }
+ u += 121665 * a[31]; out[31] = u & 127;
+ u = 19 * (u >> 7);
+ for (j = 0;j < 31;++j) { u += out[j]; out[j] = u & 255; u >>= 8; }
+ u += out[j]; out[j] = u;
}
-static inline void square(unsigned int out[32],const unsigned int a[32])
+void square(unsigned int out[32],const unsigned int a[32])
{
- unsigned int i;
- unsigned int j;
- unsigned int u;
+ unsigned int i;
+ unsigned int j;
+ unsigned int u;
- for (i = 0;i < 32;++i) {
- u = 0;
- for (j = 0;j < i - j;++j) u += a[j] * a[i - j];
- for (j = i + 1;j < i + 32 - j;++j) u += 38 * a[j] * a[i + 32 - j];
- u *= 2;
- if ((i & 1) == 0) {
- u += a[i / 2] * a[i / 2];
- u += 38 * a[i / 2 + 16] * a[i / 2 + 16];
- }
- out[i] = u;
- }
- squeeze(out);
+ for (i = 0;i < 32;++i) {
+ u = 0;
+ for (j = 0;j < i - j;++j) u += a[j] * a[i - j];
+ for (j = i + 1;j < i + 32 - j;++j) u += 38 * a[j] * a[i + 32 - j];
+ u *= 2;
+ if ((i & 1) == 0) {
+ u += a[i / 2] * a[i / 2];
+ u += 38 * a[i / 2 + 16] * a[i / 2 + 16];
+ }
+ out[i] = u;
+ }
+ squeeze(out);
}
-static inline void select(unsigned int p[64],unsigned int q[64],const unsigned int r[64],const unsigned int s[64],unsigned int b)
+void select(unsigned int p[64],unsigned int q[64],const unsigned int r[64],const unsigned int s[64],unsigned int b)
{
- unsigned int j;
- unsigned int t;
- unsigned int bminus1;
+ unsigned int j;
+ unsigned int t;
+ unsigned int bminus1;
- bminus1 = b - 1;
- for (j = 0;j < 64;++j) {
- t = bminus1 & (r[j] ^ s[j]);
- p[j] = s[j] ^ t;
- q[j] = r[j] ^ t;
- }
+ bminus1 = b - 1;
+ for (j = 0;j < 64;++j) {
+ t = bminus1 & (r[j] ^ s[j]);
+ p[j] = s[j] ^ t;
+ q[j] = r[j] ^ t;
+ }
}
static void mainloop(unsigned int work[64],const unsigned char e[32])
{
- unsigned int xzm1[64];
- unsigned int xzm[64];
- unsigned int xzmb[64];
- unsigned int xzm1b[64];
- unsigned int xznb[64];
- unsigned int xzn1b[64];
- unsigned int a0[64];
- unsigned int a1[64];
- unsigned int b0[64];
- unsigned int b1[64];
- unsigned int c1[64];
- unsigned int r[32];
- unsigned int s[32];
- unsigned int t[32];
- unsigned int u[32];
- //unsigned int i;
- unsigned int j;
- unsigned int b;
- int pos;
-
- for (j = 0;j < 32;++j) xzm1[j] = work[j];
- xzm1[32] = 1;
- for (j = 33;j < 64;++j) xzm1[j] = 0;
-
- xzm[0] = 1;
- for (j = 1;j < 64;++j) xzm[j] = 0;
-
- for (pos = 254;pos >= 0;--pos) {
- b = e[pos / 8] >> (pos & 7);
- b &= 1;
- select(xzmb,xzm1b,xzm,xzm1,b);
- add(a0,xzmb,xzmb + 32);
- sub(a0 + 32,xzmb,xzmb + 32);
- add(a1,xzm1b,xzm1b + 32);
- sub(a1 + 32,xzm1b,xzm1b + 32);
- square(b0,a0);
- square(b0 + 32,a0 + 32);
- mult(b1,a1,a0 + 32);
- mult(b1 + 32,a1 + 32,a0);
- add(c1,b1,b1 + 32);
- sub(c1 + 32,b1,b1 + 32);
- square(r,c1 + 32);
- sub(s,b0,b0 + 32);
- mult121665(t,s);
- add(u,t,b0);
- mult(xznb,b0,b0 + 32);
- mult(xznb + 32,s,u);
- square(xzn1b,c1);
- mult(xzn1b + 32,r,work);
- select(xzm,xzm1,xznb,xzn1b,b);
- }
-
- for (j = 0;j < 64;++j) work[j] = xzm[j];
+ unsigned int xzm1[64];
+ unsigned int xzm[64];
+ unsigned int xzmb[64];
+ unsigned int xzm1b[64];
+ unsigned int xznb[64];
+ unsigned int xzn1b[64];
+ unsigned int a0[64];
+ unsigned int a1[64];
+ unsigned int b0[64];
+ unsigned int b1[64];
+ unsigned int c1[64];
+ unsigned int r[32];
+ unsigned int s[32];
+ unsigned int t[32];
+ unsigned int u[32];
+ //unsigned int i;
+ unsigned int j;
+ unsigned int b;
+ int pos;
+
+ for (j = 0;j < 32;++j) xzm1[j] = work[j];
+ xzm1[32] = 1;
+ for (j = 33;j < 64;++j) xzm1[j] = 0;
+
+ xzm[0] = 1;
+ for (j = 1;j < 64;++j) xzm[j] = 0;
+
+ for (pos = 254;pos >= 0;--pos) {
+ b = e[pos / 8] >> (pos & 7);
+ b &= 1;
+ select(xzmb,xzm1b,xzm,xzm1,b);
+ add(a0,xzmb,xzmb + 32);
+ sub(a0 + 32,xzmb,xzmb + 32);
+ add(a1,xzm1b,xzm1b + 32);
+ sub(a1 + 32,xzm1b,xzm1b + 32);
+ square(b0,a0);
+ square(b0 + 32,a0 + 32);
+ mult(b1,a1,a0 + 32);
+ mult(b1 + 32,a1 + 32,a0);
+ add(c1,b1,b1 + 32);
+ sub(c1 + 32,b1,b1 + 32);
+ square(r,c1 + 32);
+ sub(s,b0,b0 + 32);
+ mult121665(t,s);
+ add(u,t,b0);
+ mult(xznb,b0,b0 + 32);
+ mult(xznb + 32,s,u);
+ square(xzn1b,c1);
+ mult(xzn1b + 32,r,work);
+ select(xzm,xzm1,xznb,xzn1b,b);
+ }
+
+ for (j = 0;j < 64;++j) work[j] = xzm[j];
}
static void recip(unsigned int out[32],const unsigned int z[32])
{
- unsigned int z2[32];
- unsigned int z9[32];
- unsigned int z11[32];
- unsigned int z2_5_0[32];
- unsigned int z2_10_0[32];
- unsigned int z2_20_0[32];
- unsigned int z2_50_0[32];
- unsigned int z2_100_0[32];
- unsigned int t0[32];
- unsigned int t1[32];
- int i;
-
- /* 2 */ square(z2,z);
- /* 4 */ square(t1,z2);
- /* 8 */ square(t0,t1);
- /* 9 */ mult(z9,t0,z);
- /* 11 */ mult(z11,z9,z2);
- /* 22 */ square(t0,z11);
- /* 2^5 - 2^0 = 31 */ mult(z2_5_0,t0,z9);
-
- /* 2^6 - 2^1 */ square(t0,z2_5_0);
- /* 2^7 - 2^2 */ square(t1,t0);
- /* 2^8 - 2^3 */ square(t0,t1);
- /* 2^9 - 2^4 */ square(t1,t0);
- /* 2^10 - 2^5 */ square(t0,t1);
- /* 2^10 - 2^0 */ mult(z2_10_0,t0,z2_5_0);
-
- /* 2^11 - 2^1 */ square(t0,z2_10_0);
- /* 2^12 - 2^2 */ square(t1,t0);
- /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t0,t1); square(t1,t0); }
- /* 2^20 - 2^0 */ mult(z2_20_0,t1,z2_10_0);
-
- /* 2^21 - 2^1 */ square(t0,z2_20_0);
- /* 2^22 - 2^2 */ square(t1,t0);
- /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { square(t0,t1); square(t1,t0); }
- /* 2^40 - 2^0 */ mult(t0,t1,z2_20_0);
-
- /* 2^41 - 2^1 */ square(t1,t0);
- /* 2^42 - 2^2 */ square(t0,t1);
- /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t1,t0); square(t0,t1); }
- /* 2^50 - 2^0 */ mult(z2_50_0,t0,z2_10_0);
-
- /* 2^51 - 2^1 */ square(t0,z2_50_0);
- /* 2^52 - 2^2 */ square(t1,t0);
- /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); }
- /* 2^100 - 2^0 */ mult(z2_100_0,t1,z2_50_0);
-
- /* 2^101 - 2^1 */ square(t1,z2_100_0);
- /* 2^102 - 2^2 */ square(t0,t1);
- /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { square(t1,t0); square(t0,t1); }
- /* 2^200 - 2^0 */ mult(t1,t0,z2_100_0);
-
- /* 2^201 - 2^1 */ square(t0,t1);
- /* 2^202 - 2^2 */ square(t1,t0);
- /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); }
- /* 2^250 - 2^0 */ mult(t0,t1,z2_50_0);
-
- /* 2^251 - 2^1 */ square(t1,t0);
- /* 2^252 - 2^2 */ square(t0,t1);
- /* 2^253 - 2^3 */ square(t1,t0);
- /* 2^254 - 2^4 */ square(t0,t1);
- /* 2^255 - 2^5 */ square(t1,t0);
- /* 2^255 - 21 */ mult(out,t1,z11);
-}
-
-static inline int crypto_scalarmult(unsigned char *q,
- const unsigned char *n,
- const unsigned char *p)
-{
- unsigned int work[96];
- unsigned char e[32];
- unsigned int i;
- for (i = 0;i < 32;++i) e[i] = n[i];
- e[0] &= 248;
- e[31] &= 127;
- e[31] |= 64;
- for (i = 0;i < 32;++i) work[i] = p[i];
- mainloop(work,e);
- recip(work + 32,work + 32);
- mult(work + 64,work,work + 32);
- freeze(work + 64);
- for (i = 0;i < 32;++i) q[i] = work[64 + i];
- return 0;
+ unsigned int z2[32];
+ unsigned int z9[32];
+ unsigned int z11[32];
+ unsigned int z2_5_0[32];
+ unsigned int z2_10_0[32];
+ unsigned int z2_20_0[32];
+ unsigned int z2_50_0[32];
+ unsigned int z2_100_0[32];
+ unsigned int t0[32];
+ unsigned int t1[32];
+ int i;
+
+ /* 2 */ square(z2,z);
+ /* 4 */ square(t1,z2);
+ /* 8 */ square(t0,t1);
+ /* 9 */ mult(z9,t0,z);
+ /* 11 */ mult(z11,z9,z2);
+ /* 22 */ square(t0,z11);
+ /* 2^5 - 2^0 = 31 */ mult(z2_5_0,t0,z9);
+
+ /* 2^6 - 2^1 */ square(t0,z2_5_0);
+ /* 2^7 - 2^2 */ square(t1,t0);
+ /* 2^8 - 2^3 */ square(t0,t1);
+ /* 2^9 - 2^4 */ square(t1,t0);
+ /* 2^10 - 2^5 */ square(t0,t1);
+ /* 2^10 - 2^0 */ mult(z2_10_0,t0,z2_5_0);
+
+ /* 2^11 - 2^1 */ square(t0,z2_10_0);
+ /* 2^12 - 2^2 */ square(t1,t0);
+ /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t0,t1); square(t1,t0); }
+ /* 2^20 - 2^0 */ mult(z2_20_0,t1,z2_10_0);
+
+ /* 2^21 - 2^1 */ square(t0,z2_20_0);
+ /* 2^22 - 2^2 */ square(t1,t0);
+ /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { square(t0,t1); square(t1,t0); }
+ /* 2^40 - 2^0 */ mult(t0,t1,z2_20_0);
+
+ /* 2^41 - 2^1 */ square(t1,t0);
+ /* 2^42 - 2^2 */ square(t0,t1);
+ /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t1,t0); square(t0,t1); }
+ /* 2^50 - 2^0 */ mult(z2_50_0,t0,z2_10_0);
+
+ /* 2^51 - 2^1 */ square(t0,z2_50_0);
+ /* 2^52 - 2^2 */ square(t1,t0);
+ /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); }
+ /* 2^100 - 2^0 */ mult(z2_100_0,t1,z2_50_0);
+
+ /* 2^101 - 2^1 */ square(t1,z2_100_0);
+ /* 2^102 - 2^2 */ square(t0,t1);
+ /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { square(t1,t0); square(t0,t1); }
+ /* 2^200 - 2^0 */ mult(t1,t0,z2_100_0);
+
+ /* 2^201 - 2^1 */ square(t0,t1);
+ /* 2^202 - 2^2 */ square(t1,t0);
+ /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); }
+ /* 2^250 - 2^0 */ mult(t0,t1,z2_50_0);
+
+ /* 2^251 - 2^1 */ square(t1,t0);
+ /* 2^252 - 2^2 */ square(t0,t1);
+ /* 2^253 - 2^3 */ square(t1,t0);
+ /* 2^254 - 2^4 */ square(t0,t1);
+ /* 2^255 - 2^5 */ square(t1,t0);
+ /* 2^255 - 21 */ mult(out,t1,z11);
+}
+
+int crypto_scalarmult(unsigned char *q,const unsigned char *n,const unsigned char *p)
+{
+ unsigned int work[96];
+ unsigned char e[32];
+ unsigned int i;
+ for (i = 0;i < 32;++i) e[i] = n[i];
+ e[0] &= 248;
+ e[31] &= 127;
+ e[31] |= 64;
+ for (i = 0;i < 32;++i) work[i] = p[i];
+ mainloop(work,e);
+ recip(work + 32,work + 32);
+ mult(work + 64,work,work + 32);
+ freeze(work + 64);
+ for (i = 0;i < 32;++i) q[i] = work[64 + i];
+ return 0;
}
static const unsigned char base[32] = {9};
-
-static inline int crypto_scalarmult_base(unsigned char *q,
- const unsigned char *n)
+int crypto_scalarmult_base(unsigned char *q,const unsigned char *n)
{
- return crypto_scalarmult(q,n,base);
+ return crypto_scalarmult(q,n,base);
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
-// This is the Ed25519 stuff from SUPERCOP:
-// http://bench.cr.yp.to/supercop.html
+// Ed25519 ref from: http://bench.cr.yp.to/supercop.html
-// Also public domain, newer version than the Ed25519 found in NaCl
-
-typedef struct
+typedef struct
{
- crypto_uint32 v[32];
+ crypto_uint32 v[32];
}
fe25519;
+typedef struct
+{
+ crypto_uint32 v[32];
+}
+sc25519;
+
+typedef struct
+{
+ crypto_uint32 v[16];
+}
+shortsc25519;
+
+typedef struct
+{
+ fe25519 x;
+ fe25519 y;
+ fe25519 z;
+ fe25519 t;
+} ge25519;
+
+#define ge25519_p3 ge25519
+
+typedef struct
+{
+ fe25519 x;
+ fe25519 z;
+ fe25519 y;
+ fe25519 t;
+} ge25519_p1p1;
+
+typedef struct
+{
+ fe25519 x;
+ fe25519 y;
+ fe25519 z;
+} ge25519_p2;
+
+typedef struct
+{
+ fe25519 x;
+ fe25519 y;
+} ge25519_aff;
+
static void fe25519_sub(fe25519 *r, const fe25519 *x, const fe25519 *y);
-static inline crypto_uint32 equal(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
+crypto_uint32 equal(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
{
- crypto_uint32 x = a ^ b; /* 0: yes; 1..65535: no */
- x -= 1; /* 4294967295: yes; 0..65534: no */
- x >>= 31; /* 1: yes; 0: no */
- return x;
+ crypto_uint32 x = a ^ b; /* 0: yes; 1..65535: no */
+ x -= 1; /* 4294967295: yes; 0..65534: no */
+ x >>= 31; /* 1: yes; 0: no */
+ return x;
}
-static inline crypto_uint32 ge(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
+crypto_uint32 ge(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
{
- unsigned int x = a;
- x -= (unsigned int) b; /* 0..65535: yes; 4294901761..4294967295: no */
- x >>= 31; /* 0: yes; 1: no */
- x ^= 1; /* 1: yes; 0: no */
- return x;
+ unsigned int x = a;
+ x -= (unsigned int) b; /* 0..65535: yes; 4294901761..4294967295: no */
+ x >>= 31; /* 0: yes; 1: no */
+ x ^= 1; /* 1: yes; 0: no */
+ return x;
}
-static inline crypto_uint32 times19(crypto_uint32 a)
+crypto_uint32 times19(crypto_uint32 a)
{
- return (a << 4) + (a << 1) + a;
+ return (a << 4) + (a << 1) + a;
}
-static inline crypto_uint32 times38(crypto_uint32 a)
+crypto_uint32 times38(crypto_uint32 a)
{
- return (a << 5) + (a << 2) + (a << 1);
+ return (a << 5) + (a << 2) + (a << 1);
}
-static inline void reduce_add_sub(fe25519 *r)
+void reduce_add_sub(fe25519 *r)
{
- crypto_uint32 t;
- int i,rep;
+ crypto_uint32 t;
+ int i,rep;
- for(rep=0;rep<4;rep++)
- {
- t = r->v[31] >> 7;
- r->v[31] &= 127;
- t = times19(t);
- r->v[0] += t;
- for(i=0;i<31;i++)
- {
- t = r->v[i] >> 8;
- r->v[i+1] += t;
- r->v[i] &= 255;
- }
- }
+ for(rep=0;rep<4;rep++)
+ {
+ t = r->v[31] >> 7;
+ r->v[31] &= 127;
+ t = times19(t);
+ r->v[0] += t;
+ for(i=0;i<31;i++)
+ {
+ t = r->v[i] >> 8;
+ r->v[i+1] += t;
+ r->v[i] &= 255;
+ }
+ }
}
-static inline void reduce_mul(fe25519 *r)
+void reduce_mul(fe25519 *r)
{
- crypto_uint32 t;
- int i,rep;
+ crypto_uint32 t;
+ int i,rep;
- for(rep=0;rep<2;rep++)
- {
- t = r->v[31] >> 7;
- r->v[31] &= 127;
- t = times19(t);
- r->v[0] += t;
- for(i=0;i<31;i++)
- {
- t = r->v[i] >> 8;
- r->v[i+1] += t;
- r->v[i] &= 255;
- }
- }
+ for(rep=0;rep<2;rep++)
+ {
+ t = r->v[31] >> 7;
+ r->v[31] &= 127;
+ t = times19(t);
+ r->v[0] += t;
+ for(i=0;i<31;i++)
+ {
+ t = r->v[i] >> 8;
+ r->v[i+1] += t;
+ r->v[i] &= 255;
+ }
+ }
}
/* reduction modulo 2^255-19 */
-static inline void fe25519_freeze(fe25519 *r)
+void fe25519_freeze(fe25519 *r)
{
- int i;
- crypto_uint32 m = equal(r->v[31],127);
- for(i=30;i>0;i--)
- m &= equal(r->v[i],255);
- m &= ge(r->v[0],237);
+ int i;
+ crypto_uint32 m = equal(r->v[31],127);
+ for(i=30;i>0;i--)
+ m &= equal(r->v[i],255);
+ m &= ge(r->v[0],237);
- m = -m;
+ m = -m;
- r->v[31] -= m&127;
- for(i=30;i>0;i--)
- r->v[i] -= m&255;
- r->v[0] -= m&237;
+ r->v[31] -= m&127;
+ for(i=30;i>0;i--)
+ r->v[i] -= m&255;
+ r->v[0] -= m&237;
}
-static inline void fe25519_unpack(fe25519 *r, const unsigned char x[32])
+void fe25519_unpack(fe25519 *r, const unsigned char x[32])
{
- int i;
- for(i=0;i<32;i++) r->v[i] = x[i];
- r->v[31] &= 127;
+ int i;
+ for(i=0;i<32;i++) r->v[i] = x[i];
+ r->v[31] &= 127;
}
/* Assumes input x being reduced below 2^255 */
-static inline void fe25519_pack(unsigned char r[32], const fe25519 *x)
+void fe25519_pack(unsigned char r[32], const fe25519 *x)
{
- int i;
- fe25519 y = *x;
- fe25519_freeze(&y);
- for(i=0;i<32;i++)
- r[i] = y.v[i];
+ int i;
+ fe25519 y = *x;
+ fe25519_freeze(&y);
+ for(i=0;i<32;i++)
+ r[i] = y.v[i];
}
-#if 0
-static int fe25519_iszero(const fe25519 *x)
-{
- int i;
- int r;
- fe25519 t = *x;
- fe25519_freeze(&t);
- r = equal(t.v[0],0);
- for(i=1;i<32;i++)
- r &= equal(t.v[i],0);
- return r;
-}
-#endif
-
-static inline int fe25519_iseq_vartime(const fe25519 *x, const fe25519 *y)
+int fe25519_iseq_vartime(const fe25519 *x, const fe25519 *y)
{
- int i;
- fe25519 t1 = *x;
- fe25519 t2 = *y;
- fe25519_freeze(&t1);
- fe25519_freeze(&t2);
- for(i=0;i<32;i++)
- if(t1.v[i] != t2.v[i]) return 0;
- return 1;
+ int i;
+ fe25519 t1 = *x;
+ fe25519 t2 = *y;
+ fe25519_freeze(&t1);
+ fe25519_freeze(&t2);
+ for(i=0;i<32;i++)
+ if(t1.v[i] != t2.v[i]) return 0;
+ return 1;
}
-static inline void fe25519_cmov(fe25519 *r, const fe25519 *x, unsigned char b)
+void fe25519_cmov(fe25519 *r, const fe25519 *x, unsigned char b)
{
- int i;
- crypto_uint32 mask = b;
- mask = -mask;
- for(i=0;i<32;i++) r->v[i] ^= mask & (x->v[i] ^ r->v[i]);
+ int i;
+ crypto_uint32 mask = b;
+ mask = -mask;
+ for(i=0;i<32;i++) r->v[i] ^= mask & (x->v[i] ^ r->v[i]);
}
-static inline unsigned char fe25519_getparity(const fe25519 *x)
+unsigned char fe25519_getparity(const fe25519 *x)
{
- fe25519 t = *x;
- fe25519_freeze(&t);
- return t.v[0] & 1;
+ fe25519 t = *x;
+ fe25519_freeze(&t);
+ return t.v[0] & 1;
}
-static inline void fe25519_setone(fe25519 *r)
+void fe25519_setone(fe25519 *r)
{
- int i;
- r->v[0] = 1;
- for(i=1;i<32;i++) r->v[i]=0;
+ int i;
+ r->v[0] = 1;
+ for(i=1;i<32;i++) r->v[i]=0;
}
-static inline void fe25519_setzero(fe25519 *r)
+void fe25519_setzero(fe25519 *r)
{
- int i;
- for(i=0;i<32;i++) r->v[i]=0;
+ int i;
+ for(i=0;i<32;i++) r->v[i]=0;
}
-static inline void fe25519_neg(fe25519 *r, const fe25519 *x)
+void fe25519_neg(fe25519 *r, const fe25519 *x)
{
- fe25519 t;
- int i;
- for(i=0;i<32;i++) t.v[i]=x->v[i];
- fe25519_setzero(r);
- fe25519_sub(r, r, &t);
+ fe25519 t;
+ int i;
+ for(i=0;i<32;i++) t.v[i]=x->v[i];
+ fe25519_setzero(r);
+ fe25519_sub(r, r, &t);
}
-static inline void fe25519_add(fe25519 *r, const fe25519 *x, const fe25519 *y)
+void fe25519_add(fe25519 *r, const fe25519 *x, const fe25519 *y)
{
- int i;
- for(i=0;i<32;i++) r->v[i] = x->v[i] + y->v[i];
- reduce_add_sub(r);
+ int i;
+ for(i=0;i<32;i++) r->v[i] = x->v[i] + y->v[i];
+ reduce_add_sub(r);
}
-static inline void fe25519_sub(fe25519 *r, const fe25519 *x, const fe25519 *y)
+void fe25519_sub(fe25519 *r, const fe25519 *x, const fe25519 *y)
{
- int i;
- crypto_uint32 t[32];
- t[0] = x->v[0] + 0x1da;
- t[31] = x->v[31] + 0xfe;
- for(i=1;i<31;i++) t[i] = x->v[i] + 0x1fe;
- for(i=0;i<32;i++) r->v[i] = t[i] - y->v[i];
- reduce_add_sub(r);
+ int i;
+ crypto_uint32 t[32];
+ t[0] = x->v[0] + 0x1da;
+ t[31] = x->v[31] + 0xfe;
+ for(i=1;i<31;i++) t[i] = x->v[i] + 0x1fe;
+ for(i=0;i<32;i++) r->v[i] = t[i] - y->v[i];
+ reduce_add_sub(r);
}
-static inline void fe25519_mul(fe25519 *r, const fe25519 *x, const fe25519 *y)
+void fe25519_mul(fe25519 *r, const fe25519 *x, const fe25519 *y)
{
- int i,j;
- crypto_uint32 t[63];
- for(i=0;i<63;i++)t[i] = 0;
+ int i,j;
+ crypto_uint32 t[63];
+ for(i=0;i<63;i++)t[i] = 0;
- for(i=0;i<32;i++)
- for(j=0;j<32;j++)
- t[i+j] += x->v[i] * y->v[j];
+ for(i=0;i<32;i++)
+ for(j=0;j<32;j++)
+ t[i+j] += x->v[i] * y->v[j];
- for(i=32;i<63;i++)
- r->v[i-32] = t[i-32] + times38(t[i]);
- r->v[31] = t[31]; /* result now in r[0]...r[31] */
+ for(i=32;i<63;i++)
+ r->v[i-32] = t[i-32] + times38(t[i]);
+ r->v[31] = t[31]; /* result now in r[0]...r[31] */
- reduce_mul(r);
+ reduce_mul(r);
}
-static inline void fe25519_square(fe25519 *r, const fe25519 *x)
+void fe25519_square(fe25519 *r, const fe25519 *x)
{
- fe25519_mul(r, x, x);
+ fe25519_mul(r, x, x);
}
-static void fe25519_invert(fe25519 *r, const fe25519 *x)
+void fe25519_invert(fe25519 *r, const fe25519 *x)
{
- fe25519 z2;
- fe25519 z9;
- fe25519 z11;
- fe25519 z2_5_0;
- fe25519 z2_10_0;
- fe25519 z2_20_0;
- fe25519 z2_50_0;
- fe25519 z2_100_0;
- fe25519 t0;
- fe25519 t1;
- int i;
-
- /* 2 */ fe25519_square(&z2,x);
- /* 4 */ fe25519_square(&t1,&z2);
- /* 8 */ fe25519_square(&t0,&t1);
- /* 9 */ fe25519_mul(&z9,&t0,x);
- /* 11 */ fe25519_mul(&z11,&z9,&z2);
- /* 22 */ fe25519_square(&t0,&z11);
- /* 2^5 - 2^0 = 31 */ fe25519_mul(&z2_5_0,&t0,&z9);
+ fe25519 z2;
+ fe25519 z9;
+ fe25519 z11;
+ fe25519 z2_5_0;
+ fe25519 z2_10_0;
+ fe25519 z2_20_0;
+ fe25519 z2_50_0;
+ fe25519 z2_100_0;
+ fe25519 t0;
+ fe25519 t1;
+ int i;
- /* 2^6 - 2^1 */ fe25519_square(&t0,&z2_5_0);
- /* 2^7 - 2^2 */ fe25519_square(&t1,&t0);
- /* 2^8 - 2^3 */ fe25519_square(&t0,&t1);
- /* 2^9 - 2^4 */ fe25519_square(&t1,&t0);
- /* 2^10 - 2^5 */ fe25519_square(&t0,&t1);
- /* 2^10 - 2^0 */ fe25519_mul(&z2_10_0,&t0,&z2_5_0);
+ /* 2 */ fe25519_square(&z2,x);
+ /* 4 */ fe25519_square(&t1,&z2);
+ /* 8 */ fe25519_square(&t0,&t1);
+ /* 9 */ fe25519_mul(&z9,&t0,x);
+ /* 11 */ fe25519_mul(&z11,&z9,&z2);
+ /* 22 */ fe25519_square(&t0,&z11);
+ /* 2^5 - 2^0 = 31 */ fe25519_mul(&z2_5_0,&t0,&z9);
- /* 2^11 - 2^1 */ fe25519_square(&t0,&z2_10_0);
- /* 2^12 - 2^2 */ fe25519_square(&t1,&t0);
- /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
- /* 2^20 - 2^0 */ fe25519_mul(&z2_20_0,&t1,&z2_10_0);
+ /* 2^6 - 2^1 */ fe25519_square(&t0,&z2_5_0);
+ /* 2^7 - 2^2 */ fe25519_square(&t1,&t0);
+ /* 2^8 - 2^3 */ fe25519_square(&t0,&t1);
+ /* 2^9 - 2^4 */ fe25519_square(&t1,&t0);
+ /* 2^10 - 2^5 */ fe25519_square(&t0,&t1);
+ /* 2^10 - 2^0 */ fe25519_mul(&z2_10_0,&t0,&z2_5_0);
- /* 2^21 - 2^1 */ fe25519_square(&t0,&z2_20_0);
- /* 2^22 - 2^2 */ fe25519_square(&t1,&t0);
- /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
- /* 2^40 - 2^0 */ fe25519_mul(&t0,&t1,&z2_20_0);
+ /* 2^11 - 2^1 */ fe25519_square(&t0,&z2_10_0);
+ /* 2^12 - 2^2 */ fe25519_square(&t1,&t0);
+ /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
+ /* 2^20 - 2^0 */ fe25519_mul(&z2_20_0,&t1,&z2_10_0);
- /* 2^41 - 2^1 */ fe25519_square(&t1,&t0);
- /* 2^42 - 2^2 */ fe25519_square(&t0,&t1);
- /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { fe25519_square(&t1,&t0); fe25519_square(&t0,&t1); }
- /* 2^50 - 2^0 */ fe25519_mul(&z2_50_0,&t0,&z2_10_0);
+ /* 2^21 - 2^1 */ fe25519_square(&t0,&z2_20_0);
+ /* 2^22 - 2^2 */ fe25519_square(&t1,&t0);
+ /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
+ /* 2^40 - 2^0 */ fe25519_mul(&t0,&t1,&z2_20_0);
- /* 2^51 - 2^1 */ fe25519_square(&t0,&z2_50_0);
- /* 2^52 - 2^2 */ fe25519_square(&t1,&t0);
- /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
- /* 2^100 - 2^0 */ fe25519_mul(&z2_100_0,&t1,&z2_50_0);
+ /* 2^41 - 2^1 */ fe25519_square(&t1,&t0);
+ /* 2^42 - 2^2 */ fe25519_square(&t0,&t1);
+ /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { fe25519_square(&t1,&t0); fe25519_square(&t0,&t1); }
+ /* 2^50 - 2^0 */ fe25519_mul(&z2_50_0,&t0,&z2_10_0);
- /* 2^101 - 2^1 */ fe25519_square(&t1,&z2_100_0);
- /* 2^102 - 2^2 */ fe25519_square(&t0,&t1);
- /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { fe25519_square(&t1,&t0); fe25519_square(&t0,&t1); }
- /* 2^200 - 2^0 */ fe25519_mul(&t1,&t0,&z2_100_0);
-
- /* 2^201 - 2^1 */ fe25519_square(&t0,&t1);
- /* 2^202 - 2^2 */ fe25519_square(&t1,&t0);
- /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
- /* 2^250 - 2^0 */ fe25519_mul(&t0,&t1,&z2_50_0);
+ /* 2^51 - 2^1 */ fe25519_square(&t0,&z2_50_0);
+ /* 2^52 - 2^2 */ fe25519_square(&t1,&t0);
+ /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
+ /* 2^100 - 2^0 */ fe25519_mul(&z2_100_0,&t1,&z2_50_0);
- /* 2^251 - 2^1 */ fe25519_square(&t1,&t0);
- /* 2^252 - 2^2 */ fe25519_square(&t0,&t1);
- /* 2^253 - 2^3 */ fe25519_square(&t1,&t0);
- /* 2^254 - 2^4 */ fe25519_square(&t0,&t1);
- /* 2^255 - 2^5 */ fe25519_square(&t1,&t0);
- /* 2^255 - 21 */ fe25519_mul(r,&t1,&z11);
-}
-
-static void fe25519_pow2523(fe25519 *r, const fe25519 *x)
-{
- fe25519 z2;
- fe25519 z9;
- fe25519 z11;
- fe25519 z2_5_0;
- fe25519 z2_10_0;
- fe25519 z2_20_0;
- fe25519 z2_50_0;
- fe25519 z2_100_0;
- fe25519 t;
- int i;
-
- /* 2 */ fe25519_square(&z2,x);
- /* 4 */ fe25519_square(&t,&z2);
- /* 8 */ fe25519_square(&t,&t);
- /* 9 */ fe25519_mul(&z9,&t,x);
- /* 11 */ fe25519_mul(&z11,&z9,&z2);
- /* 22 */ fe25519_square(&t,&z11);
- /* 2^5 - 2^0 = 31 */ fe25519_mul(&z2_5_0,&t,&z9);
-
- /* 2^6 - 2^1 */ fe25519_square(&t,&z2_5_0);
- /* 2^10 - 2^5 */ for (i = 1;i < 5;i++) { fe25519_square(&t,&t); }
- /* 2^10 - 2^0 */ fe25519_mul(&z2_10_0,&t,&z2_5_0);
-
- /* 2^11 - 2^1 */ fe25519_square(&t,&z2_10_0);
- /* 2^20 - 2^10 */ for (i = 1;i < 10;i++) { fe25519_square(&t,&t); }
- /* 2^20 - 2^0 */ fe25519_mul(&z2_20_0,&t,&z2_10_0);
-
- /* 2^21 - 2^1 */ fe25519_square(&t,&z2_20_0);
- /* 2^40 - 2^20 */ for (i = 1;i < 20;i++) { fe25519_square(&t,&t); }
- /* 2^40 - 2^0 */ fe25519_mul(&t,&t,&z2_20_0);
-
- /* 2^41 - 2^1 */ fe25519_square(&t,&t);
- /* 2^50 - 2^10 */ for (i = 1;i < 10;i++) { fe25519_square(&t,&t); }
- /* 2^50 - 2^0 */ fe25519_mul(&z2_50_0,&t,&z2_10_0);
-
- /* 2^51 - 2^1 */ fe25519_square(&t,&z2_50_0);
- /* 2^100 - 2^50 */ for (i = 1;i < 50;i++) { fe25519_square(&t,&t); }
- /* 2^100 - 2^0 */ fe25519_mul(&z2_100_0,&t,&z2_50_0);
-
- /* 2^101 - 2^1 */ fe25519_square(&t,&z2_100_0);
- /* 2^200 - 2^100 */ for (i = 1;i < 100;i++) { fe25519_square(&t,&t); }
- /* 2^200 - 2^0 */ fe25519_mul(&t,&t,&z2_100_0);
-
- /* 2^201 - 2^1 */ fe25519_square(&t,&t);
- /* 2^250 - 2^50 */ for (i = 1;i < 50;i++) { fe25519_square(&t,&t); }
- /* 2^250 - 2^0 */ fe25519_mul(&t,&t,&z2_50_0);
-
- /* 2^251 - 2^1 */ fe25519_square(&t,&t);
- /* 2^252 - 2^2 */ fe25519_square(&t,&t);
- /* 2^252 - 3 */ fe25519_mul(r,&t,x);
-}
-
-typedef struct
-{
- crypto_uint32 v[32];
-}
-sc25519;
-
-typedef struct
-{
- crypto_uint32 v[16];
-}
-shortsc25519;
+ /* 2^101 - 2^1 */ fe25519_square(&t1,&z2_100_0);
+ /* 2^102 - 2^2 */ fe25519_square(&t0,&t1);
+ /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { fe25519_square(&t1,&t0); fe25519_square(&t0,&t1); }
+ /* 2^200 - 2^0 */ fe25519_mul(&t1,&t0,&z2_100_0);
-static const crypto_uint32 m[32] = {0xED, 0xD3, 0xF5, 0x5C, 0x1A, 0x63, 0x12, 0x58, 0xD6, 0x9C, 0xF7, 0xA2, 0xDE, 0xF9, 0xDE, 0x14,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
+ /* 2^201 - 2^1 */ fe25519_square(&t0,&t1);
+ /* 2^202 - 2^2 */ fe25519_square(&t1,&t0);
+ /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
+ /* 2^250 - 2^0 */ fe25519_mul(&t0,&t1,&z2_50_0);
-static const crypto_uint32 mu[33] = {0x1B, 0x13, 0x2C, 0x0A, 0xA3, 0xE5, 0x9C, 0xED, 0xA7, 0x29, 0x63, 0x08, 0x5D, 0x21, 0x06, 0x21,
- 0xEB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F};
+ /* 2^251 - 2^1 */ fe25519_square(&t1,&t0);
+ /* 2^252 - 2^2 */ fe25519_square(&t0,&t1);
+ /* 2^253 - 2^3 */ fe25519_square(&t1,&t0);
+ /* 2^254 - 2^4 */ fe25519_square(&t0,&t1);
+ /* 2^255 - 2^5 */ fe25519_square(&t1,&t0);
+ /* 2^255 - 21 */ fe25519_mul(r,&t1,&z11);
+}
-static inline crypto_uint32 lt(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
+void fe25519_pow2523(fe25519 *r, const fe25519 *x)
+{
+ fe25519 z2;
+ fe25519 z9;
+ fe25519 z11;
+ fe25519 z2_5_0;
+ fe25519 z2_10_0;
+ fe25519 z2_20_0;
+ fe25519 z2_50_0;
+ fe25519 z2_100_0;
+ fe25519 t;
+ int i;
+
+ /* 2 */ fe25519_square(&z2,x);
+ /* 4 */ fe25519_square(&t,&z2);
+ /* 8 */ fe25519_square(&t,&t);
+ /* 9 */ fe25519_mul(&z9,&t,x);
+ /* 11 */ fe25519_mul(&z11,&z9,&z2);
+ /* 22 */ fe25519_square(&t,&z11);
+ /* 2^5 - 2^0 = 31 */ fe25519_mul(&z2_5_0,&t,&z9);
+
+ /* 2^6 - 2^1 */ fe25519_square(&t,&z2_5_0);
+ /* 2^10 - 2^5 */ for (i = 1;i < 5;i++) { fe25519_square(&t,&t); }
+ /* 2^10 - 2^0 */ fe25519_mul(&z2_10_0,&t,&z2_5_0);
+
+ /* 2^11 - 2^1 */ fe25519_square(&t,&z2_10_0);
+ /* 2^20 - 2^10 */ for (i = 1;i < 10;i++) { fe25519_square(&t,&t); }
+ /* 2^20 - 2^0 */ fe25519_mul(&z2_20_0,&t,&z2_10_0);
+
+ /* 2^21 - 2^1 */ fe25519_square(&t,&z2_20_0);
+ /* 2^40 - 2^20 */ for (i = 1;i < 20;i++) { fe25519_square(&t,&t); }
+ /* 2^40 - 2^0 */ fe25519_mul(&t,&t,&z2_20_0);
+
+ /* 2^41 - 2^1 */ fe25519_square(&t,&t);
+ /* 2^50 - 2^10 */ for (i = 1;i < 10;i++) { fe25519_square(&t,&t); }
+ /* 2^50 - 2^0 */ fe25519_mul(&z2_50_0,&t,&z2_10_0);
+
+ /* 2^51 - 2^1 */ fe25519_square(&t,&z2_50_0);
+ /* 2^100 - 2^50 */ for (i = 1;i < 50;i++) { fe25519_square(&t,&t); }
+ /* 2^100 - 2^0 */ fe25519_mul(&z2_100_0,&t,&z2_50_0);
+
+ /* 2^101 - 2^1 */ fe25519_square(&t,&z2_100_0);
+ /* 2^200 - 2^100 */ for (i = 1;i < 100;i++) { fe25519_square(&t,&t); }
+ /* 2^200 - 2^0 */ fe25519_mul(&t,&t,&z2_100_0);
+
+ /* 2^201 - 2^1 */ fe25519_square(&t,&t);
+ /* 2^250 - 2^50 */ for (i = 1;i < 50;i++) { fe25519_square(&t,&t); }
+ /* 2^250 - 2^0 */ fe25519_mul(&t,&t,&z2_50_0);
+
+ /* 2^251 - 2^1 */ fe25519_square(&t,&t);
+ /* 2^252 - 2^2 */ fe25519_square(&t,&t);
+ /* 2^252 - 3 */ fe25519_mul(r,&t,x);
+}
+
+static const crypto_uint32 m[32] = {0xED, 0xD3, 0xF5, 0x5C, 0x1A, 0x63, 0x12, 0x58, 0xD6, 0x9C, 0xF7, 0xA2, 0xDE, 0xF9, 0xDE, 0x14,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
+
+static const crypto_uint32 mu[33] = {0x1B, 0x13, 0x2C, 0x0A, 0xA3, 0xE5, 0x9C, 0xED, 0xA7, 0x29, 0x63, 0x08, 0x5D, 0x21, 0x06, 0x21,
+ 0xEB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F};
+
+crypto_uint32 lt(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
{
- unsigned int x = a;
- x -= (unsigned int) b; /* 0..65535: no; 4294901761..4294967295: yes */
- x >>= 31; /* 0: no; 1: yes */
- return x;
+ unsigned int x = a;
+ x -= (unsigned int) b; /* 0..65535: no; 4294901761..4294967295: yes */
+ x >>= 31; /* 0: no; 1: yes */
+ return x;
}
/* Reduce coefficients of r before calling reduce_add_sub */
-static inline void reduce_add_sub(sc25519 *r)
-{
- crypto_uint32 pb = 0;
- crypto_uint32 b;
- crypto_uint32 mask;
- int i;
- unsigned char t[32];
-
- for(i=0;i<32;i++)
- {
- pb += m[i];
- b = lt(r->v[i],pb);
- t[i] = r->v[i]-pb+(b<<8);
- pb = b;
- }
- mask = b - 1;
- for(i=0;i<32;i++)
- r->v[i] ^= mask & (r->v[i] ^ t[i]);
+void reduce_add_sub(sc25519 *r)
+{
+ crypto_uint32 pb = 0;
+ crypto_uint32 b;
+ crypto_uint32 mask;
+ int i;
+ unsigned char t[32];
+
+ for(i=0;i<32;i++)
+ {
+ pb += m[i];
+ b = lt(r->v[i],pb);
+ t[i] = r->v[i]-pb+(b<<8);
+ pb = b;
+ }
+ mask = b - 1;
+ for(i=0;i<32;i++)
+ r->v[i] ^= mask & (r->v[i] ^ t[i]);
}
/* Reduce coefficients of x before calling barrett_reduce */
-static inline void barrett_reduce(sc25519 *r, const crypto_uint32 x[64])
-{
- /* See HAC, Alg. 14.42 */
- int i,j;
- crypto_uint32 q2[66];
- crypto_uint32 *q3 = q2 + 33;
- crypto_uint32 r1[33];
- crypto_uint32 r2[33];
- crypto_uint32 carry;
- crypto_uint32 pb = 0;
- crypto_uint32 b;
-
- for (i = 0;i < 66;++i) q2[i] = 0;
- for (i = 0;i < 33;++i) r2[i] = 0;
-
- for(i=0;i<33;i++)
- for(j=0;j<33;j++)
- if(i+j >= 31) q2[i+j] += mu[i]*x[j+31];
- carry = q2[31] >> 8;
- q2[32] += carry;
- carry = q2[32] >> 8;
- q2[33] += carry;
-
- for(i=0;i<33;i++)r1[i] = x[i];
- for(i=0;i<32;i++)
- for(j=0;j<33;j++)
- if(i+j < 33) r2[i+j] += m[i]*q3[j];
-
- for(i=0;i<32;i++)
- {
- carry = r2[i] >> 8;
- r2[i+1] += carry;
- r2[i] &= 0xff;
- }
-
- for(i=0;i<32;i++)
- {
- pb += r2[i];
- b = lt(r1[i],pb);
- r->v[i] = r1[i]-pb+(b<<8);
- pb = b;
- }
-
- /* XXX: Can it really happen that r<0?, See HAC, Alg 14.42, Step 3
- * If so: Handle it here!
- */
-
- reduce_add_sub(r);
- reduce_add_sub(r);
-}
-
-static inline void sc25519_from32bytes(sc25519 *r, const unsigned char x[32])
-{
- int i;
- crypto_uint32 t[64];
- for(i=0;i<32;i++) t[i] = x[i];
- for(i=32;i<64;++i) t[i] = 0;
- barrett_reduce(r, t);
-}
-
-#if 0
-static void shortsc25519_from16bytes(shortsc25519 *r, const unsigned char x[16])
-{
- int i;
- for(i=0;i<16;i++) r->v[i] = x[i];
-}
-#endif
-
-static inline void sc25519_from64bytes(sc25519 *r, const unsigned char x[64])
-{
- int i;
- crypto_uint32 t[64];
- for(i=0;i<64;i++) t[i] = x[i];
- barrett_reduce(r, t);
-}
+void barrett_reduce(sc25519 *r, const crypto_uint32 x[64])
+{
+ /* See HAC, Alg. 14.42 */
+ int i,j;
+ crypto_uint32 q2[66];
+ crypto_uint32 *q3 = q2 + 33;
+ crypto_uint32 r1[33];
+ crypto_uint32 r2[33];
+ crypto_uint32 carry;
+ crypto_uint32 pb = 0;
+ crypto_uint32 b;
+
+ for (i = 0;i < 66;++i) q2[i] = 0;
+ for (i = 0;i < 33;++i) r2[i] = 0;
+
+ for(i=0;i<33;i++)
+ for(j=0;j<33;j++)
+ if(i+j >= 31) q2[i+j] += mu[i]*x[j+31];
+ carry = q2[31] >> 8;
+ q2[32] += carry;
+ carry = q2[32] >> 8;
+ q2[33] += carry;
+
+ for(i=0;i<33;i++)r1[i] = x[i];
+ for(i=0;i<32;i++)
+ for(j=0;j<33;j++)
+ if(i+j < 33) r2[i+j] += m[i]*q3[j];
+
+ for(i=0;i<32;i++)
+ {
+ carry = r2[i] >> 8;
+ r2[i+1] += carry;
+ r2[i] &= 0xff;
+ }
-#if 0
-static void sc25519_from_shortsc(sc25519 *r, const shortsc25519 *x)
-{
- int i;
- for(i=0;i<16;i++)
- r->v[i] = x->v[i];
- for(i=0;i<16;i++)
- r->v[16+i] = 0;
-}
-#endif
+ for(i=0;i<32;i++)
+ {
+ pb += r2[i];
+ b = lt(r1[i],pb);
+ r->v[i] = r1[i]-pb+(b<<8);
+ pb = b;
+ }
-static inline void sc25519_to32bytes(unsigned char r[32], const sc25519 *x)
-{
- int i;
- for(i=0;i<32;i++) r[i] = x->v[i];
-}
+ /* XXX: Can it really happen that r<0?, See HAC, Alg 14.42, Step 3
+ * If so: Handle it here!
+ */
-#if 0
-static int sc25519_iszero_vartime(const sc25519 *x)
-{
- int i;
- for(i=0;i<32;i++)
- if(x->v[i] != 0) return 0;
- return 1;
+ reduce_add_sub(r);
+ reduce_add_sub(r);
}
-#endif
-#if 0
-static int sc25519_isshort_vartime(const sc25519 *x)
+void sc25519_from32bytes(sc25519 *r, const unsigned char x[32])
{
- int i;
- for(i=31;i>15;i--)
- if(x->v[i] != 0) return 0;
- return 1;
+ int i;
+ crypto_uint32 t[64];
+ for(i=0;i<32;i++) t[i] = x[i];
+ for(i=32;i<64;++i) t[i] = 0;
+ barrett_reduce(r, t);
}
-#endif
-#if 0
-static int sc25519_lt_vartime(const sc25519 *x, const sc25519 *y)
+void sc25519_from64bytes(sc25519 *r, const unsigned char x[64])
{
- int i;
- for(i=31;i>=0;i--)
- {
- if(x->v[i] < y->v[i]) return 1;
- if(x->v[i] > y->v[i]) return 0;
- }
- return 0;
+ int i;
+ crypto_uint32 t[64];
+ for(i=0;i<64;i++) t[i] = x[i];
+ barrett_reduce(r, t);
}
-#endif
-static inline void sc25519_add(sc25519 *r, const sc25519 *x, const sc25519 *y)
+void sc25519_to32bytes(unsigned char r[32], const sc25519 *x)
{
- int i, carry;
- for(i=0;i<32;i++) r->v[i] = x->v[i] + y->v[i];
- for(i=0;i<31;i++)
- {
- carry = r->v[i] >> 8;
- r->v[i+1] += carry;
- r->v[i] &= 0xff;
- }
- reduce_add_sub(r);
+ int i;
+ for(i=0;i<32;i++) r[i] = x->v[i];
}
-#if 0
-static void sc25519_sub_nored(sc25519 *r, const sc25519 *x, const sc25519 *y)
+void sc25519_add(sc25519 *r, const sc25519 *x, const sc25519 *y)
{
- crypto_uint32 b = 0;
- crypto_uint32 t;
- int i;
- for(i=0;i<32;i++)
- {
- t = x->v[i] - y->v[i] - b;
- r->v[i] = t & 255;
- b = (t >> 8) & 1;
- }
+ int i, carry;
+ for(i=0;i<32;i++) r->v[i] = x->v[i] + y->v[i];
+ for(i=0;i<31;i++)
+ {
+ carry = r->v[i] >> 8;
+ r->v[i+1] += carry;
+ r->v[i] &= 0xff;
+ }
+ reduce_add_sub(r);
}
-#endif
-static inline void sc25519_mul(sc25519 *r, const sc25519 *x, const sc25519 *y)
+void sc25519_mul(sc25519 *r, const sc25519 *x, const sc25519 *y)
{
- int i,j,carry;
- crypto_uint32 t[64];
- for(i=0;i<64;i++)t[i] = 0;
+ int i,j,carry;
+ crypto_uint32 t[64];
+ for(i=0;i<64;i++)t[i] = 0;
- for(i=0;i<32;i++)
- for(j=0;j<32;j++)
- t[i+j] += x->v[i] * y->v[j];
+ for(i=0;i<32;i++)
+ for(j=0;j<32;j++)
+ t[i+j] += x->v[i] * y->v[j];
- /* Reduce coefficients */
- for(i=0;i<63;i++)
- {
- carry = t[i] >> 8;
- t[i+1] += carry;
- t[i] &= 0xff;
- }
-
- barrett_reduce(r, t);
-}
-
-#if 0
-static void sc25519_mul_shortsc(sc25519 *r, const sc25519 *x, const shortsc25519 *y)
-{
- sc25519 t;
- sc25519_from_shortsc(&t, y);
- sc25519_mul(r, x, &t);
-}
-#endif
+ /* Reduce coefficients */
+ for(i=0;i<63;i++)
+ {
+ carry = t[i] >> 8;
+ t[i+1] += carry;
+ t[i] &= 0xff;
+ }
-static inline void sc25519_window3(signed char r[85], const sc25519 *s)
-{
- char carry;
- int i;
- for(i=0;i<10;i++)
- {
- r[8*i+0] = s->v[3*i+0] & 7;
- r[8*i+1] = (s->v[3*i+0] >> 3) & 7;
- r[8*i+2] = (s->v[3*i+0] >> 6) & 7;
- r[8*i+2] ^= (s->v[3*i+1] << 2) & 7;
- r[8*i+3] = (s->v[3*i+1] >> 1) & 7;
- r[8*i+4] = (s->v[3*i+1] >> 4) & 7;
- r[8*i+5] = (s->v[3*i+1] >> 7) & 7;
- r[8*i+5] ^= (s->v[3*i+2] << 1) & 7;
- r[8*i+6] = (s->v[3*i+2] >> 2) & 7;
- r[8*i+7] = (s->v[3*i+2] >> 5) & 7;
- }
- r[8*i+0] = s->v[3*i+0] & 7;
- r[8*i+1] = (s->v[3*i+0] >> 3) & 7;
- r[8*i+2] = (s->v[3*i+0] >> 6) & 7;
- r[8*i+2] ^= (s->v[3*i+1] << 2) & 7;
- r[8*i+3] = (s->v[3*i+1] >> 1) & 7;
- r[8*i+4] = (s->v[3*i+1] >> 4) & 7;
-
- /* Making it signed */
- carry = 0;
- for(i=0;i<84;i++)
- {
- r[i] += carry;
- r[i+1] += r[i] >> 3;
- r[i] &= 7;
- carry = r[i] >> 2;
- r[i] -= carry<<3;
- }
- r[84] += carry;
-}
-
-#if 0
-static void sc25519_window5(signed char r[51], const sc25519 *s)
-{
- char carry;
- int i;
- for(i=0;i<6;i++)
- {
- r[8*i+0] = s->v[5*i+0] & 31;
- r[8*i+1] = (s->v[5*i+0] >> 5) & 31;
- r[8*i+1] ^= (s->v[5*i+1] << 3) & 31;
- r[8*i+2] = (s->v[5*i+1] >> 2) & 31;
- r[8*i+3] = (s->v[5*i+1] >> 7) & 31;
- r[8*i+3] ^= (s->v[5*i+2] << 1) & 31;
- r[8*i+4] = (s->v[5*i+2] >> 4) & 31;
- r[8*i+4] ^= (s->v[5*i+3] << 4) & 31;
- r[8*i+5] = (s->v[5*i+3] >> 1) & 31;
- r[8*i+6] = (s->v[5*i+3] >> 6) & 31;
- r[8*i+6] ^= (s->v[5*i+4] << 2) & 31;
- r[8*i+7] = (s->v[5*i+4] >> 3) & 31;
- }
- r[8*i+0] = s->v[5*i+0] & 31;
- r[8*i+1] = (s->v[5*i+0] >> 5) & 31;
- r[8*i+1] ^= (s->v[5*i+1] << 3) & 31;
- r[8*i+2] = (s->v[5*i+1] >> 2) & 31;
-
- /* Making it signed */
- carry = 0;
- for(i=0;i<50;i++)
- {
- r[i] += carry;
- r[i+1] += r[i] >> 5;
- r[i] &= 31;
- carry = r[i] >> 4;
- r[i] -= carry<<5;
- }
- r[50] += carry;
+ barrett_reduce(r, t);
+}
+
+void sc25519_window3(signed char r[85], const sc25519 *s)
+{
+ char carry;
+ int i;
+ for(i=0;i<10;i++)
+ {
+ r[8*i+0] = s->v[3*i+0] & 7;
+ r[8*i+1] = (s->v[3*i+0] >> 3) & 7;
+ r[8*i+2] = (s->v[3*i+0] >> 6) & 7;
+ r[8*i+2] ^= (s->v[3*i+1] << 2) & 7;
+ r[8*i+3] = (s->v[3*i+1] >> 1) & 7;
+ r[8*i+4] = (s->v[3*i+1] >> 4) & 7;
+ r[8*i+5] = (s->v[3*i+1] >> 7) & 7;
+ r[8*i+5] ^= (s->v[3*i+2] << 1) & 7;
+ r[8*i+6] = (s->v[3*i+2] >> 2) & 7;
+ r[8*i+7] = (s->v[3*i+2] >> 5) & 7;
+ }
+ r[8*i+0] = s->v[3*i+0] & 7;
+ r[8*i+1] = (s->v[3*i+0] >> 3) & 7;
+ r[8*i+2] = (s->v[3*i+0] >> 6) & 7;
+ r[8*i+2] ^= (s->v[3*i+1] << 2) & 7;
+ r[8*i+3] = (s->v[3*i+1] >> 1) & 7;
+ r[8*i+4] = (s->v[3*i+1] >> 4) & 7;
+
+ /* Making it signed */
+ carry = 0;
+ for(i=0;i<84;i++)
+ {
+ r[i] += carry;
+ r[i+1] += r[i] >> 3;
+ r[i] &= 7;
+ carry = r[i] >> 2;
+ r[i] -= carry<<3;
+ }
+ r[84] += carry;
}
-#endif
-static inline void sc25519_2interleave2(unsigned char r[127], const sc25519 *s1, const sc25519 *s2)
+void sc25519_2interleave2(unsigned char r[127], const sc25519 *s1, const sc25519 *s2)
{
- int i;
- for(i=0;i<31;i++)
- {
- r[4*i] = ( s1->v[i] & 3) ^ (( s2->v[i] & 3) << 2);
- r[4*i+1] = ((s1->v[i] >> 2) & 3) ^ (((s2->v[i] >> 2) & 3) << 2);
- r[4*i+2] = ((s1->v[i] >> 4) & 3) ^ (((s2->v[i] >> 4) & 3) << 2);
- r[4*i+3] = ((s1->v[i] >> 6) & 3) ^ (((s2->v[i] >> 6) & 3) << 2);
- }
- r[124] = ( s1->v[31] & 3) ^ (( s2->v[31] & 3) << 2);
- r[125] = ((s1->v[31] >> 2) & 3) ^ (((s2->v[31] >> 2) & 3) << 2);
- r[126] = ((s1->v[31] >> 4) & 3) ^ (((s2->v[31] >> 4) & 3) << 2);
+ int i;
+ for(i=0;i<31;i++)
+ {
+ r[4*i] = ( s1->v[i] & 3) ^ (( s2->v[i] & 3) << 2);
+ r[4*i+1] = ((s1->v[i] >> 2) & 3) ^ (((s2->v[i] >> 2) & 3) << 2);
+ r[4*i+2] = ((s1->v[i] >> 4) & 3) ^ (((s2->v[i] >> 4) & 3) << 2);
+ r[4*i+3] = ((s1->v[i] >> 6) & 3) ^ (((s2->v[i] >> 6) & 3) << 2);
+ }
+ r[124] = ( s1->v[31] & 3) ^ (( s2->v[31] & 3) << 2);
+ r[125] = ((s1->v[31] >> 2) & 3) ^ (((s2->v[31] >> 2) & 3) << 2);
+ r[126] = ((s1->v[31] >> 4) & 3) ^ (((s2->v[31] >> 4) & 3) << 2);
}
-typedef struct
-{
- fe25519 x;
- fe25519 y;
- fe25519 z;
- fe25519 t;
-} ge25519;
-
/* d */
-static const fe25519 ge25519_ecd = {{0xA3, 0x78, 0x59, 0x13, 0xCA, 0x4D, 0xEB, 0x75, 0xAB, 0xD8, 0x41, 0x41, 0x4D, 0x0A, 0x70, 0x00,
- 0x98, 0xE8, 0x79, 0x77, 0x79, 0x40, 0xC7, 0x8C, 0x73, 0xFE, 0x6F, 0x2B, 0xEE, 0x6C, 0x03, 0x52}};
+static const fe25519 ge25519_ecd = {{0xA3, 0x78, 0x59, 0x13, 0xCA, 0x4D, 0xEB, 0x75, 0xAB, 0xD8, 0x41, 0x41, 0x4D, 0x0A, 0x70, 0x00,
+ 0x98, 0xE8, 0x79, 0x77, 0x79, 0x40, 0xC7, 0x8C, 0x73, 0xFE, 0x6F, 0x2B, 0xEE, 0x6C, 0x03, 0x52}};
/* 2*d */
-static const fe25519 ge25519_ec2d = {{0x59, 0xF1, 0xB2, 0x26, 0x94, 0x9B, 0xD6, 0xEB, 0x56, 0xB1, 0x83, 0x82, 0x9A, 0x14, 0xE0, 0x00,
- 0x30, 0xD1, 0xF3, 0xEE, 0xF2, 0x80, 0x8E, 0x19, 0xE7, 0xFC, 0xDF, 0x56, 0xDC, 0xD9, 0x06, 0x24}};
+static const fe25519 ge25519_ec2d = {{0x59, 0xF1, 0xB2, 0x26, 0x94, 0x9B, 0xD6, 0xEB, 0x56, 0xB1, 0x83, 0x82, 0x9A, 0x14, 0xE0, 0x00,
+ 0x30, 0xD1, 0xF3, 0xEE, 0xF2, 0x80, 0x8E, 0x19, 0xE7, 0xFC, 0xDF, 0x56, 0xDC, 0xD9, 0x06, 0x24}};
/* sqrt(-1) */
-static const fe25519 ge25519_sqrtm1 = {{0xB0, 0xA0, 0x0E, 0x4A, 0x27, 0x1B, 0xEE, 0xC4, 0x78, 0xE4, 0x2F, 0xAD, 0x06, 0x18, 0x43, 0x2F,
- 0xA7, 0xD7, 0xFB, 0x3D, 0x99, 0x00, 0x4D, 0x2B, 0x0B, 0xDF, 0xC1, 0x4F, 0x80, 0x24, 0x83, 0x2B}};
-
-#define ge25519_p3 ge25519
-
-typedef struct
-{
- fe25519 x;
- fe25519 z;
- fe25519 y;
- fe25519 t;
-} ge25519_p1p1;
-
-typedef struct
-{
- fe25519 x;
- fe25519 y;
- fe25519 z;
-} ge25519_p2;
-
-typedef struct
-{
- fe25519 x;
- fe25519 y;
-} ge25519_aff;
-
+static const fe25519 ge25519_sqrtm1 = {{0xB0, 0xA0, 0x0E, 0x4A, 0x27, 0x1B, 0xEE, 0xC4, 0x78, 0xE4, 0x2F, 0xAD, 0x06, 0x18, 0x43, 0x2F,
+ 0xA7, 0xD7, 0xFB, 0x3D, 0x99, 0x00, 0x4D, 0x2B, 0x0B, 0xDF, 0xC1, 0x4F, 0x80, 0x24, 0x83, 0x2B}};
/* Packed coordinates of the base point */
-static const ge25519 ge25519_base = {{{0x1A, 0xD5, 0x25, 0x8F, 0x60, 0x2D, 0x56, 0xC9, 0xB2, 0xA7, 0x25, 0x95, 0x60, 0xC7, 0x2C, 0x69,
- 0x5C, 0xDC, 0xD6, 0xFD, 0x31, 0xE2, 0xA4, 0xC0, 0xFE, 0x53, 0x6E, 0xCD, 0xD3, 0x36, 0x69, 0x21}},
- {{0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
- 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}},
- {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
- {{0xA3, 0xDD, 0xB7, 0xA5, 0xB3, 0x8A, 0xDE, 0x6D, 0xF5, 0x52, 0x51, 0x77, 0x80, 0x9F, 0xF0, 0x20,
- 0x7D, 0xE3, 0xAB, 0x64, 0x8E, 0x4E, 0xEA, 0x66, 0x65, 0x76, 0x8B, 0xD7, 0x0F, 0x5F, 0x87, 0x67}}};
+static const ge25519 ge25519_base = {{{0x1A, 0xD5, 0x25, 0x8F, 0x60, 0x2D, 0x56, 0xC9, 0xB2, 0xA7, 0x25, 0x95, 0x60, 0xC7, 0x2C, 0x69,
+ 0x5C, 0xDC, 0xD6, 0xFD, 0x31, 0xE2, 0xA4, 0xC0, 0xFE, 0x53, 0x6E, 0xCD, 0xD3, 0x36, 0x69, 0x21}},
+ {{0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}},
+ {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ {{0xA3, 0xDD, 0xB7, 0xA5, 0xB3, 0x8A, 0xDE, 0x6D, 0xF5, 0x52, 0x51, 0x77, 0x80, 0x9F, 0xF0, 0x20,
+ 0x7D, 0xE3, 0xAB, 0x64, 0x8E, 0x4E, 0xEA, 0x66, 0x65, 0x76, 0x8B, 0xD7, 0x0F, 0x5F, 0x87, 0x67}}};
/* Multiples of the base point in affine representation */
static const ge25519_aff ge25519_base_multiples_affine[425] = {
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x1a, 0xd5, 0x25, 0x8f, 0x60, 0x2d, 0x56, 0xc9, 0xb2, 0xa7, 0x25, 0x95, 0x60, 0xc7, 0x2c, 0x69, 0x5c, 0xdc, 0xd6, 0xfd, 0x31, 0xe2, 0xa4, 0xc0, 0xfe, 0x53, 0x6e, 0xcd, 0xd3, 0x36, 0x69, 0x21}} ,
{{0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}}},
@@ -1026,7 +887,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xd4, 0xb4, 0xf5, 0x78, 0x48, 0x68, 0xc3, 0x02, 0x04, 0x03, 0x24, 0x67, 0x17, 0xec, 0x16, 0x9f, 0xf7, 0x9e, 0x26, 0x60, 0x8e, 0xa1, 0x26, 0xa1, 0xab, 0x69, 0xee, 0x77, 0xd1, 0xb1, 0x67, 0x12}}},
{{{0x70, 0xf8, 0xc9, 0xc4, 0x57, 0xa6, 0x3a, 0x49, 0x47, 0x15, 0xce, 0x93, 0xc1, 0x9e, 0x73, 0x1a, 0xf9, 0x20, 0x35, 0x7a, 0xb8, 0xd4, 0x25, 0x83, 0x46, 0xf1, 0xcf, 0x56, 0xdb, 0xa8, 0x3d, 0x20}} ,
{{0x2f, 0x11, 0x32, 0xca, 0x61, 0xab, 0x38, 0xdf, 0xf0, 0x0f, 0x2f, 0xea, 0x32, 0x28, 0xf2, 0x4c, 0x6c, 0x71, 0xd5, 0x80, 0x85, 0xb8, 0x0e, 0x47, 0xe1, 0x95, 0x15, 0xcb, 0x27, 0xe8, 0xd0, 0x47}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xc8, 0x84, 0xa5, 0x08, 0xbc, 0xfd, 0x87, 0x3b, 0x99, 0x8b, 0x69, 0x80, 0x7b, 0xc6, 0x3a, 0xeb, 0x93, 0xcf, 0x4e, 0xf8, 0x5c, 0x2d, 0x86, 0x42, 0xb6, 0x71, 0xd7, 0x97, 0x5f, 0xe1, 0x42, 0x67}} ,
{{0xb4, 0xb9, 0x37, 0xfc, 0xa9, 0x5b, 0x2f, 0x1e, 0x93, 0xe4, 0x1e, 0x62, 0xfc, 0x3c, 0x78, 0x81, 0x8f, 0xf3, 0x8a, 0x66, 0x09, 0x6f, 0xad, 0x6e, 0x79, 0x73, 0xe5, 0xc9, 0x00, 0x06, 0xd3, 0x21}}},
@@ -1036,7 +897,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xf1, 0x79, 0x7b, 0xed, 0x4f, 0x44, 0xb2, 0xe7, 0x08, 0x0d, 0xc2, 0x08, 0x12, 0xd2, 0x9f, 0xdf, 0xcd, 0x93, 0x20, 0x8a, 0xcf, 0x33, 0xca, 0x6d, 0x89, 0xb9, 0x77, 0xc8, 0x93, 0x1b, 0x4e, 0x60}}},
{{{0x26, 0x4f, 0x7e, 0x97, 0xf6, 0x40, 0xdd, 0x4f, 0xfc, 0x52, 0x78, 0xf9, 0x90, 0x31, 0x03, 0xe6, 0x7d, 0x56, 0x39, 0x0b, 0x1d, 0x56, 0x82, 0x85, 0xf9, 0x1a, 0x42, 0x17, 0x69, 0x6c, 0xcf, 0x39}} ,
{{0x69, 0xd2, 0x06, 0x3a, 0x4f, 0x39, 0x2d, 0xf9, 0x38, 0x40, 0x8c, 0x4c, 0xe7, 0x05, 0x12, 0xb4, 0x78, 0x8b, 0xf8, 0xc0, 0xec, 0x93, 0xde, 0x7a, 0x6b, 0xce, 0x2c, 0xe1, 0x0e, 0xa9, 0x34, 0x44}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x0b, 0xa4, 0x3c, 0xb0, 0x0f, 0x7a, 0x51, 0xf1, 0x78, 0xd6, 0xd9, 0x6a, 0xfd, 0x46, 0xe8, 0xb8, 0xa8, 0x79, 0x1d, 0x87, 0xf9, 0x90, 0xf2, 0x9c, 0x13, 0x29, 0xf8, 0x0b, 0x20, 0x64, 0xfa, 0x05}} ,
{{0x26, 0x09, 0xda, 0x17, 0xaf, 0x95, 0xd6, 0xfb, 0x6a, 0x19, 0x0d, 0x6e, 0x5e, 0x12, 0xf1, 0x99, 0x4c, 0xaa, 0xa8, 0x6f, 0x79, 0x86, 0xf4, 0x72, 0x28, 0x00, 0x26, 0xf9, 0xea, 0x9e, 0x19, 0x3d}}},
@@ -1046,7 +907,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x1c, 0x59, 0x1c, 0x65, 0x5d, 0x34, 0xa4, 0x09, 0xcd, 0x13, 0x9c, 0x70, 0x7d, 0xb1, 0x2a, 0xc5, 0x88, 0xaf, 0x0b, 0x60, 0xc7, 0x9f, 0x34, 0x8d, 0xd6, 0xb7, 0x7f, 0xea, 0x78, 0x65, 0x8d, 0x77}}},
{{{0x56, 0xa5, 0xc2, 0x0c, 0xdd, 0xbc, 0xb8, 0x20, 0x6d, 0x57, 0x61, 0xb5, 0xfb, 0x78, 0xb5, 0xd4, 0x49, 0x54, 0x90, 0x26, 0xc1, 0xcb, 0xe9, 0xe6, 0xbf, 0xec, 0x1d, 0x4e, 0xed, 0x07, 0x7e, 0x5e}} ,
{{0xc7, 0xf6, 0x6c, 0x56, 0x31, 0x20, 0x14, 0x0e, 0xa8, 0xd9, 0x27, 0xc1, 0x9a, 0x3d, 0x1b, 0x7d, 0x0e, 0x26, 0xd3, 0x81, 0xaa, 0xeb, 0xf5, 0x6b, 0x79, 0x02, 0xf1, 0x51, 0x5c, 0x75, 0x55, 0x0f}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x0a, 0x34, 0xcd, 0x82, 0x3c, 0x33, 0x09, 0x54, 0xd2, 0x61, 0x39, 0x30, 0x9b, 0xfd, 0xef, 0x21, 0x26, 0xd4, 0x70, 0xfa, 0xee, 0xf9, 0x31, 0x33, 0x73, 0x84, 0xd0, 0xb3, 0x81, 0xbf, 0xec, 0x2e}} ,
{{0xe8, 0x93, 0x8b, 0x00, 0x64, 0xf7, 0x9c, 0xb8, 0x74, 0xe0, 0xe6, 0x49, 0x48, 0x4d, 0x4d, 0x48, 0xb6, 0x19, 0xa1, 0x40, 0xb7, 0xd9, 0x32, 0x41, 0x7c, 0x82, 0x37, 0xa1, 0x2d, 0xdc, 0xd2, 0x54}}},
@@ -1056,7 +917,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xbc, 0x58, 0xe6, 0xc0, 0x95, 0x2a, 0x2a, 0x81, 0x9a, 0x7a, 0xf3, 0xd2, 0x06, 0xbe, 0x48, 0xbc, 0x0c, 0xc5, 0x46, 0xe0, 0x6a, 0xd4, 0xac, 0x0f, 0xd9, 0xcc, 0x82, 0x34, 0x2c, 0xaf, 0xdb, 0x1f}}},
{{{0xf7, 0x17, 0x13, 0xbd, 0xfb, 0xbc, 0xd2, 0xec, 0x45, 0xb3, 0x15, 0x31, 0xe9, 0xaf, 0x82, 0x84, 0x3d, 0x28, 0xc6, 0xfc, 0x11, 0xf5, 0x41, 0xb5, 0x8b, 0xd3, 0x12, 0x76, 0x52, 0xe7, 0x1a, 0x3c}} ,
{{0x4e, 0x36, 0x11, 0x07, 0xa2, 0x15, 0x20, 0x51, 0xc4, 0x2a, 0xc3, 0x62, 0x8b, 0x5e, 0x7f, 0xa6, 0x0f, 0xf9, 0x45, 0x85, 0x6c, 0x11, 0x86, 0xb7, 0x7e, 0xe5, 0xd7, 0xf9, 0xc3, 0x91, 0x1c, 0x05}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xea, 0xd6, 0xde, 0x29, 0x3a, 0x00, 0xb9, 0x02, 0x59, 0xcb, 0x26, 0xc4, 0xba, 0x99, 0xb1, 0x97, 0x2f, 0x8e, 0x00, 0x92, 0x26, 0x4f, 0x52, 0xeb, 0x47, 0x1b, 0x89, 0x8b, 0x24, 0xc0, 0x13, 0x7d}} ,
{{0xd5, 0x20, 0x5b, 0x80, 0xa6, 0x80, 0x20, 0x95, 0xc3, 0xe9, 0x9f, 0x8e, 0x87, 0x9e, 0x1e, 0x9e, 0x7a, 0xc7, 0xcc, 0x75, 0x6c, 0xa5, 0xf1, 0x91, 0x1a, 0xa8, 0x01, 0x2c, 0xab, 0x76, 0xa9, 0x59}}},
@@ -1066,7 +927,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x22, 0xf5, 0x5f, 0x4d, 0x15, 0xef, 0xfc, 0x4e, 0x57, 0x03, 0x36, 0x89, 0xf0, 0xeb, 0x5b, 0x91, 0xd6, 0xe2, 0xca, 0x01, 0xa5, 0xee, 0x52, 0xec, 0xa0, 0x3c, 0x8f, 0x33, 0x90, 0x5a, 0x94, 0x72}}},
{{{0x8a, 0x4b, 0xe7, 0x38, 0xbc, 0xda, 0xc2, 0xb0, 0x85, 0xe1, 0x4a, 0xfe, 0x2d, 0x44, 0x84, 0xcb, 0x20, 0x6b, 0x2d, 0xbf, 0x11, 0x9c, 0xd7, 0xbe, 0xd3, 0x3e, 0x5f, 0xbf, 0x68, 0xbc, 0xa8, 0x07}} ,
{{0x01, 0x89, 0x28, 0x22, 0x6a, 0x78, 0xaa, 0x29, 0x03, 0xc8, 0x74, 0x95, 0x03, 0x3e, 0xdc, 0xbd, 0x07, 0x13, 0xa8, 0xa2, 0x20, 0x2d, 0xb3, 0x18, 0x70, 0x42, 0xfd, 0x7a, 0xc4, 0xd7, 0x49, 0x72}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x02, 0xff, 0x32, 0x2b, 0x5c, 0x93, 0x54, 0x32, 0xe8, 0x57, 0x54, 0x1a, 0x8b, 0x33, 0x60, 0x65, 0xd3, 0x67, 0xa4, 0xc1, 0x26, 0xc4, 0xa4, 0x34, 0x1f, 0x9b, 0xa7, 0xa9, 0xf4, 0xd9, 0x4f, 0x5b}} ,
{{0x46, 0x8d, 0xb0, 0x33, 0x54, 0x26, 0x5b, 0x68, 0xdf, 0xbb, 0xc5, 0xec, 0xc2, 0xf9, 0x3c, 0x5a, 0x37, 0xc1, 0x8e, 0x27, 0x47, 0xaa, 0x49, 0x5a, 0xf8, 0xfb, 0x68, 0x04, 0x23, 0xd1, 0xeb, 0x40}}},
@@ -1076,7 +937,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x60, 0xd6, 0xdd, 0x78, 0xe6, 0xd4, 0x22, 0x42, 0x1f, 0x00, 0xf9, 0xb1, 0x6a, 0x63, 0xe2, 0x92, 0x59, 0xd1, 0x1a, 0xb7, 0x00, 0x54, 0x29, 0xc9, 0xc1, 0xf6, 0x6f, 0x7a, 0xc5, 0x3c, 0x5f, 0x65}}},
{{{0x27, 0x4f, 0xd0, 0x72, 0xb1, 0x11, 0x14, 0x27, 0x15, 0x94, 0x48, 0x81, 0x7e, 0x74, 0xd8, 0x32, 0xd5, 0xd1, 0x11, 0x28, 0x60, 0x63, 0x36, 0x32, 0x37, 0xb5, 0x13, 0x1c, 0xa0, 0x37, 0xe3, 0x74}} ,
{{0xf1, 0x25, 0x4e, 0x11, 0x96, 0x67, 0xe6, 0x1c, 0xc2, 0xb2, 0x53, 0xe2, 0xda, 0x85, 0xee, 0xb2, 0x9f, 0x59, 0xf3, 0xba, 0xbd, 0xfa, 0xcf, 0x6e, 0xf9, 0xda, 0xa4, 0xb3, 0x02, 0x8f, 0x64, 0x08}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x34, 0x94, 0xf2, 0x64, 0x54, 0x47, 0x37, 0x07, 0x40, 0x8a, 0x20, 0xba, 0x4a, 0x55, 0xd7, 0x3f, 0x47, 0xba, 0x25, 0x23, 0x14, 0xb0, 0x2c, 0xe8, 0x55, 0xa8, 0xa6, 0xef, 0x51, 0xbd, 0x6f, 0x6a}} ,
{{0x71, 0xd6, 0x16, 0x76, 0xb2, 0x06, 0xea, 0x79, 0xf5, 0xc4, 0xc3, 0x52, 0x7e, 0x61, 0xd1, 0xe1, 0xad, 0x70, 0x78, 0x1d, 0x16, 0x11, 0xf8, 0x7c, 0x2b, 0xfc, 0x55, 0x9f, 0x52, 0xf8, 0xf5, 0x16}}},
@@ -1086,7 +947,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x02, 0xcc, 0x22, 0x74, 0x4d, 0x19, 0x07, 0xc0, 0xda, 0xb5, 0x76, 0x51, 0x2a, 0xaa, 0xa6, 0x0a, 0x5f, 0x26, 0xd4, 0xbc, 0xaf, 0x48, 0x88, 0x7f, 0x02, 0xbc, 0xf2, 0xe1, 0xcf, 0xe9, 0xdd, 0x15}}},
{{{0xed, 0xb5, 0x9a, 0x8c, 0x9a, 0xdd, 0x27, 0xf4, 0x7f, 0x47, 0xd9, 0x52, 0xa7, 0xcd, 0x65, 0xa5, 0x31, 0x22, 0xed, 0xa6, 0x63, 0x5b, 0x80, 0x4a, 0xad, 0x4d, 0xed, 0xbf, 0xee, 0x49, 0xb3, 0x06}} ,
{{0xf8, 0x64, 0x8b, 0x60, 0x90, 0xe9, 0xde, 0x44, 0x77, 0xb9, 0x07, 0x36, 0x32, 0xc2, 0x50, 0xf5, 0x65, 0xdf, 0x48, 0x4c, 0x37, 0xaa, 0x68, 0xab, 0x9a, 0x1f, 0x3e, 0xff, 0x89, 0x92, 0xa0, 0x07}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x7d, 0x4f, 0x9c, 0x19, 0xc0, 0x4a, 0x31, 0xec, 0xf9, 0xaa, 0xeb, 0xb2, 0x16, 0x9c, 0xa3, 0x66, 0x5f, 0xd1, 0xd4, 0xed, 0xb8, 0x92, 0x1c, 0xab, 0xda, 0xea, 0xd9, 0x57, 0xdf, 0x4c, 0x2a, 0x48}} ,
{{0x4b, 0xb0, 0x4e, 0x6e, 0x11, 0x3b, 0x51, 0xbd, 0x6a, 0xfd, 0xe4, 0x25, 0xa5, 0x5f, 0x11, 0x3f, 0x98, 0x92, 0x51, 0x14, 0xc6, 0x5f, 0x3c, 0x0b, 0xa8, 0xf7, 0xc2, 0x81, 0x43, 0xde, 0x91, 0x73}}},
@@ -1096,7 +957,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x2e, 0x69, 0x98, 0xfd, 0xc6, 0xbd, 0xcc, 0xca, 0xdf, 0x9a, 0x44, 0x7e, 0x9d, 0xca, 0x89, 0x6d, 0xbf, 0x27, 0xc2, 0xf8, 0xcd, 0x46, 0x00, 0x2b, 0xb5, 0x58, 0x4e, 0xb7, 0x89, 0x09, 0xe9, 0x2d}}},
{{{0x54, 0xbe, 0x75, 0xcb, 0x05, 0xb0, 0x54, 0xb7, 0xe7, 0x26, 0x86, 0x4a, 0xfc, 0x19, 0xcf, 0x27, 0x46, 0xd4, 0x22, 0x96, 0x5a, 0x11, 0xe8, 0xd5, 0x1b, 0xed, 0x71, 0xc5, 0x5d, 0xc8, 0xaf, 0x45}} ,
{{0x40, 0x7b, 0x77, 0x57, 0x49, 0x9e, 0x80, 0x39, 0x23, 0xee, 0x81, 0x0b, 0x22, 0xcf, 0xdb, 0x7a, 0x2f, 0x14, 0xb8, 0x57, 0x8f, 0xa1, 0x39, 0x1e, 0x77, 0xfc, 0x0b, 0xa6, 0xbf, 0x8a, 0x0c, 0x6c}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x77, 0x3a, 0xd4, 0xd8, 0x27, 0xcf, 0xe8, 0xa1, 0x72, 0x9d, 0xca, 0xdd, 0x0d, 0x96, 0xda, 0x79, 0xed, 0x56, 0x42, 0x15, 0x60, 0xc7, 0x1c, 0x6b, 0x26, 0x30, 0xf6, 0x6a, 0x95, 0x67, 0xf3, 0x0a}} ,
{{0xc5, 0x08, 0xa4, 0x2b, 0x2f, 0xbd, 0x31, 0x81, 0x2a, 0xa6, 0xb6, 0xe4, 0x00, 0x91, 0xda, 0x3d, 0xb2, 0xb0, 0x96, 0xce, 0x8a, 0xd2, 0x8d, 0x70, 0xb3, 0xd3, 0x34, 0x01, 0x90, 0x8d, 0x10, 0x21}}},
@@ -1106,7 +967,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x61, 0x36, 0xd6, 0x08, 0x90, 0xbf, 0xa7, 0x7a, 0x97, 0x6c, 0x0f, 0x84, 0xd5, 0x33, 0x2d, 0x37, 0xc9, 0x6a, 0x80, 0x90, 0x3d, 0x0a, 0xa2, 0xaa, 0xe1, 0xb8, 0x84, 0xba, 0x61, 0x36, 0xdd, 0x69}}},
{{{0x6b, 0xdb, 0x5b, 0x9c, 0xc6, 0x92, 0xbc, 0x23, 0xaf, 0xc5, 0xb8, 0x75, 0xf8, 0x42, 0xfa, 0xd6, 0xb6, 0x84, 0x94, 0x63, 0x98, 0x93, 0x48, 0x78, 0x38, 0xcd, 0xbb, 0x18, 0x34, 0xc3, 0xdb, 0x67}} ,
{{0x96, 0xf3, 0x3a, 0x09, 0x56, 0xb0, 0x6f, 0x7c, 0x51, 0x1e, 0x1b, 0x39, 0x48, 0xea, 0xc9, 0x0c, 0x25, 0xa2, 0x7a, 0xca, 0xe7, 0x92, 0xfc, 0x59, 0x30, 0xa3, 0x89, 0x85, 0xdf, 0x6f, 0x43, 0x38}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x79, 0x84, 0x44, 0x19, 0xbd, 0xe9, 0x54, 0xc4, 0xc0, 0x6e, 0x2a, 0xa8, 0xa8, 0x9b, 0x43, 0xd5, 0x71, 0x22, 0x5f, 0xdc, 0x01, 0xfa, 0xdf, 0xb3, 0xb8, 0x47, 0x4b, 0x0a, 0xa5, 0x44, 0xea, 0x29}} ,
{{0x05, 0x90, 0x50, 0xaf, 0x63, 0x5f, 0x9d, 0x9e, 0xe1, 0x9d, 0x38, 0x97, 0x1f, 0x6c, 0xac, 0x30, 0x46, 0xb2, 0x6a, 0x19, 0xd1, 0x4b, 0xdb, 0xbb, 0x8c, 0xda, 0x2e, 0xab, 0xc8, 0x5a, 0x77, 0x6c}}},
@@ -1116,7 +977,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x5d, 0x04, 0xb3, 0x40, 0x28, 0x95, 0x2d, 0x30, 0x83, 0xec, 0x5e, 0xe4, 0xff, 0x75, 0xfe, 0x79, 0x26, 0x9d, 0x1d, 0x36, 0xcd, 0x0a, 0x15, 0xd2, 0x24, 0x14, 0x77, 0x71, 0xd7, 0x8a, 0x1b, 0x04}}},
{{{0x5d, 0x93, 0xc9, 0xbe, 0xaa, 0x90, 0xcd, 0x9b, 0xfb, 0x73, 0x7e, 0xb0, 0x64, 0x98, 0x57, 0x44, 0x42, 0x41, 0xb1, 0xaf, 0xea, 0xc1, 0xc3, 0x22, 0xff, 0x60, 0x46, 0xcb, 0x61, 0x81, 0x70, 0x61}} ,
{{0x0d, 0x82, 0xb9, 0xfe, 0x21, 0xcd, 0xc4, 0xf5, 0x98, 0x0c, 0x4e, 0x72, 0xee, 0x87, 0x49, 0xf8, 0xa1, 0x95, 0xdf, 0x8f, 0x2d, 0xbd, 0x21, 0x06, 0x7c, 0x15, 0xe8, 0x12, 0x6d, 0x93, 0xd6, 0x38}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x91, 0xf7, 0x51, 0xd9, 0xef, 0x7d, 0x42, 0x01, 0x13, 0xe9, 0xb8, 0x7f, 0xa6, 0x49, 0x17, 0x64, 0x21, 0x80, 0x83, 0x2c, 0x63, 0x4c, 0x60, 0x09, 0x59, 0x91, 0x92, 0x77, 0x39, 0x51, 0xf4, 0x48}} ,
{{0x60, 0xd5, 0x22, 0x83, 0x08, 0x2f, 0xff, 0x99, 0x3e, 0x69, 0x6d, 0x88, 0xda, 0xe7, 0x5b, 0x52, 0x26, 0x31, 0x2a, 0xe5, 0x89, 0xde, 0x68, 0x90, 0xb6, 0x22, 0x5a, 0xbd, 0xd3, 0x85, 0x53, 0x31}}},
@@ -1126,7 +987,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xba, 0x38, 0x02, 0xf7, 0x81, 0x43, 0x83, 0xa3, 0x20, 0x4f, 0x01, 0x3b, 0x8a, 0x04, 0x38, 0x31, 0xc6, 0x0f, 0xc8, 0xdf, 0xd7, 0xfa, 0x2f, 0x88, 0x3f, 0xfc, 0x0c, 0x76, 0xc4, 0xa6, 0x45, 0x72}}},
{{{0xbb, 0x0c, 0xbc, 0x6a, 0xa4, 0x97, 0x17, 0x93, 0x2d, 0x6f, 0xde, 0x72, 0x10, 0x1c, 0x08, 0x2c, 0x0f, 0x80, 0x32, 0x68, 0x27, 0xd4, 0xab, 0xdd, 0xc5, 0x58, 0x61, 0x13, 0x6d, 0x11, 0x1e, 0x4d}} ,
{{0x1a, 0xb9, 0xc9, 0x10, 0xfb, 0x1e, 0x4e, 0xf4, 0x84, 0x4b, 0x8a, 0x5e, 0x7b, 0x4b, 0xe8, 0x43, 0x8c, 0x8f, 0x00, 0xb5, 0x54, 0x13, 0xc5, 0x5c, 0xb6, 0x35, 0x4e, 0x9d, 0xe4, 0x5b, 0x41, 0x6d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x15, 0x7d, 0x12, 0x48, 0x82, 0x14, 0x42, 0xcd, 0x32, 0xd4, 0x4b, 0xc1, 0x72, 0x61, 0x2a, 0x8c, 0xec, 0xe2, 0xf8, 0x24, 0x45, 0x94, 0xe3, 0xbe, 0xdd, 0x67, 0xa8, 0x77, 0x5a, 0xae, 0x5b, 0x4b}} ,
{{0xcb, 0x77, 0x9a, 0x20, 0xde, 0xb8, 0x23, 0xd9, 0xa0, 0x0f, 0x8c, 0x7b, 0xa5, 0xcb, 0xae, 0xb6, 0xec, 0x42, 0x67, 0x0e, 0x58, 0xa4, 0x75, 0x98, 0x21, 0x71, 0x84, 0xb3, 0xe0, 0x76, 0x94, 0x73}}},
@@ -1136,7 +997,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x32, 0xfb, 0x86, 0xec, 0x33, 0x6b, 0x2e, 0x51, 0x2b, 0xc8, 0xfa, 0x6c, 0x70, 0x47, 0x7e, 0xce, 0x05, 0x0c, 0x71, 0xf3, 0xb4, 0x56, 0xa6, 0xdc, 0xcc, 0x78, 0x07, 0x75, 0xd0, 0xdd, 0xb2, 0x6a}}},
{{{0xc6, 0xef, 0xb9, 0xc0, 0x2b, 0x22, 0x08, 0x1e, 0x71, 0x70, 0xb3, 0x35, 0x9c, 0x7a, 0x01, 0x92, 0x44, 0x9a, 0xf6, 0xb0, 0x58, 0x95, 0xc1, 0x9b, 0x02, 0xed, 0x2d, 0x7c, 0x34, 0x29, 0x49, 0x44}} ,
{{0x45, 0x62, 0x1d, 0x2e, 0xff, 0x2a, 0x1c, 0x21, 0xa4, 0x25, 0x7b, 0x0d, 0x8c, 0x15, 0x39, 0xfc, 0x8f, 0x7c, 0xa5, 0x7d, 0x1e, 0x25, 0xa3, 0x45, 0xd6, 0xab, 0xbd, 0xcb, 0xc5, 0x5e, 0x78, 0x77}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xd0, 0xd3, 0x42, 0xed, 0x1d, 0x00, 0x3c, 0x15, 0x2c, 0x9c, 0x77, 0x81, 0xd2, 0x73, 0xd1, 0x06, 0xd5, 0xc4, 0x7f, 0x94, 0xbb, 0x92, 0x2d, 0x2c, 0x4b, 0x45, 0x4b, 0xe9, 0x2a, 0x89, 0x6b, 0x2b}} ,
{{0xd2, 0x0c, 0x88, 0xc5, 0x48, 0x4d, 0xea, 0x0d, 0x4a, 0xc9, 0x52, 0x6a, 0x61, 0x79, 0xe9, 0x76, 0xf3, 0x85, 0x52, 0x5c, 0x1b, 0x2c, 0xe1, 0xd6, 0xc4, 0x0f, 0x18, 0x0e, 0x4e, 0xf6, 0x1c, 0x7f}}},
@@ -1146,7 +1007,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x4c, 0xe6, 0xb0, 0xc1, 0xa5, 0x2a, 0x82, 0x09, 0x08, 0xad, 0x79, 0x9c, 0x56, 0xf6, 0xf9, 0xc1, 0xd7, 0x7c, 0x39, 0x7f, 0x93, 0xca, 0x11, 0x55, 0xbf, 0x07, 0x1b, 0x82, 0x29, 0x69, 0x95, 0x5c}}},
{{{0x87, 0xee, 0xa6, 0x56, 0x9e, 0xc2, 0x9a, 0x56, 0x24, 0x42, 0x85, 0x4d, 0x98, 0x31, 0x1e, 0x60, 0x4d, 0x87, 0x85, 0x04, 0xae, 0x46, 0x12, 0xf9, 0x8e, 0x7f, 0xe4, 0x7f, 0xf6, 0x1c, 0x37, 0x01}} ,
{{0x73, 0x4c, 0xb6, 0xc5, 0xc4, 0xe9, 0x6c, 0x85, 0x48, 0x4a, 0x5a, 0xac, 0xd9, 0x1f, 0x43, 0xf8, 0x62, 0x5b, 0xee, 0x98, 0x2a, 0x33, 0x8e, 0x79, 0xce, 0x61, 0x06, 0x35, 0xd8, 0xd7, 0xca, 0x71}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x72, 0xd3, 0xae, 0xa6, 0xca, 0x8f, 0xcd, 0xcc, 0x78, 0x8e, 0x19, 0x4d, 0xa7, 0xd2, 0x27, 0xe9, 0xa4, 0x3c, 0x16, 0x5b, 0x84, 0x80, 0xf9, 0xd0, 0xcc, 0x6a, 0x1e, 0xca, 0x1e, 0x67, 0xbd, 0x63}} ,
{{0x7b, 0x6e, 0x2a, 0xd2, 0x87, 0x48, 0xff, 0xa1, 0xca, 0xe9, 0x15, 0x85, 0xdc, 0xdb, 0x2c, 0x39, 0x12, 0x91, 0xa9, 0x20, 0xaa, 0x4f, 0x29, 0xf4, 0x15, 0x7a, 0xd2, 0xf5, 0x32, 0xcc, 0x60, 0x04}}},
@@ -1156,7 +1017,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xbb, 0x40, 0x5e, 0xbc, 0x16, 0x92, 0x05, 0xc4, 0xc0, 0x4e, 0x72, 0x90, 0x0e, 0xab, 0xcf, 0x8a, 0xed, 0xef, 0xb9, 0x2d, 0x3b, 0xf8, 0x43, 0x5b, 0xba, 0x2d, 0xeb, 0x2f, 0x52, 0xd2, 0xd1, 0x5a}}},
{{{0x40, 0xb4, 0xab, 0xe6, 0xad, 0x9f, 0x46, 0x69, 0x4a, 0xb3, 0x8e, 0xaa, 0xea, 0x9c, 0x8a, 0x20, 0x16, 0x5d, 0x8c, 0x13, 0xbd, 0xf6, 0x1d, 0xc5, 0x24, 0xbd, 0x90, 0x2a, 0x1c, 0xc7, 0x13, 0x3b}} ,
{{0x54, 0xdc, 0x16, 0x0d, 0x18, 0xbe, 0x35, 0x64, 0x61, 0x52, 0x02, 0x80, 0xaf, 0x05, 0xf7, 0xa6, 0x42, 0xd3, 0x8f, 0x2e, 0x79, 0x26, 0xa8, 0xbb, 0xb2, 0x17, 0x48, 0xb2, 0x7a, 0x0a, 0x89, 0x14}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x20, 0xa8, 0x88, 0xe3, 0x91, 0xc0, 0x6e, 0xbb, 0x8a, 0x27, 0x82, 0x51, 0x83, 0xb2, 0x28, 0xa9, 0x83, 0xeb, 0xa6, 0xa9, 0x4d, 0x17, 0x59, 0x22, 0x54, 0x00, 0x50, 0x45, 0xcb, 0x48, 0x4b, 0x18}} ,
{{0x33, 0x7c, 0xe7, 0x26, 0xba, 0x4d, 0x32, 0xfe, 0x53, 0xf4, 0xfa, 0x83, 0xe3, 0xa5, 0x79, 0x66, 0x73, 0xef, 0x80, 0x23, 0x68, 0xc2, 0x60, 0xdd, 0xa9, 0x33, 0xdc, 0x03, 0x7a, 0xe0, 0xe0, 0x3e}}},
@@ -1166,7 +1027,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x6e, 0x44, 0xa3, 0xf9, 0x64, 0xaf, 0xe7, 0x6d, 0x7d, 0xdf, 0x1e, 0xac, 0x04, 0xea, 0x3b, 0x5f, 0x9b, 0xe8, 0x24, 0x9d, 0x0e, 0xe5, 0x2e, 0x3e, 0xdf, 0xa9, 0xf7, 0xd4, 0x50, 0x71, 0xf0, 0x78}}},
{{{0x3e, 0xa8, 0x38, 0xc2, 0x57, 0x56, 0x42, 0x9a, 0xb1, 0xe2, 0xf8, 0x45, 0xaa, 0x11, 0x48, 0x5f, 0x17, 0xc4, 0x54, 0x27, 0xdc, 0x5d, 0xaa, 0xdd, 0x41, 0xbc, 0xdf, 0x81, 0xb9, 0x53, 0xee, 0x52}} ,
{{0xc3, 0xf1, 0xa7, 0x6d, 0xb3, 0x5f, 0x92, 0x6f, 0xcc, 0x91, 0xb8, 0x95, 0x05, 0xdf, 0x3c, 0x64, 0x57, 0x39, 0x61, 0x51, 0xad, 0x8c, 0x38, 0x7b, 0xc8, 0xde, 0x00, 0x34, 0xbe, 0xa1, 0xb0, 0x7e}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x25, 0x24, 0x1d, 0x8a, 0x67, 0x20, 0xee, 0x42, 0xeb, 0x38, 0xed, 0x0b, 0x8b, 0xcd, 0x46, 0x9d, 0x5e, 0x6b, 0x1e, 0x24, 0x9d, 0x12, 0x05, 0x1a, 0xcc, 0x05, 0x4e, 0x92, 0x38, 0xe1, 0x1f, 0x50}} ,
{{0x4e, 0xee, 0x1c, 0x91, 0xe6, 0x11, 0xbd, 0x8e, 0x55, 0x1a, 0x18, 0x75, 0x66, 0xaf, 0x4d, 0x7b, 0x0f, 0xae, 0x6d, 0x85, 0xca, 0x82, 0x58, 0x21, 0x9c, 0x18, 0xe0, 0xed, 0xec, 0x22, 0x80, 0x2f}}},
@@ -1176,7 +1037,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xa1, 0x0c, 0xf3, 0x08, 0x43, 0xd9, 0x24, 0x1e, 0x83, 0xa7, 0xdf, 0x91, 0xca, 0xbd, 0x69, 0x47, 0x8d, 0x1b, 0xe2, 0xb9, 0x4e, 0xb5, 0xe1, 0x76, 0xb3, 0x1c, 0x93, 0x03, 0xce, 0x5f, 0xb3, 0x5a}}},
{{{0x1d, 0xda, 0xe4, 0x61, 0x03, 0x50, 0xa9, 0x8b, 0x68, 0x18, 0xef, 0xb2, 0x1c, 0x84, 0x3b, 0xa2, 0x44, 0x95, 0xa3, 0x04, 0x3b, 0xd6, 0x99, 0x00, 0xaf, 0x76, 0x42, 0x67, 0x02, 0x7d, 0x85, 0x56}} ,
{{0xce, 0x72, 0x0e, 0x29, 0x84, 0xb2, 0x7d, 0xd2, 0x45, 0xbe, 0x57, 0x06, 0xed, 0x7f, 0xcf, 0xed, 0xcd, 0xef, 0x19, 0xd6, 0xbc, 0x15, 0x79, 0x64, 0xd2, 0x18, 0xe3, 0x20, 0x67, 0x3a, 0x54, 0x0b}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x52, 0xfd, 0x04, 0xc5, 0xfb, 0x99, 0xe7, 0xe8, 0xfb, 0x8c, 0xe1, 0x42, 0x03, 0xef, 0x9d, 0xd9, 0x9e, 0x4d, 0xf7, 0x80, 0xcf, 0x2e, 0xcc, 0x9b, 0x45, 0xc9, 0x7b, 0x7a, 0xbc, 0x37, 0xa8, 0x52}} ,
{{0x96, 0x11, 0x41, 0x8a, 0x47, 0x91, 0xfe, 0xb6, 0xda, 0x7a, 0x54, 0x63, 0xd1, 0x14, 0x35, 0x05, 0x86, 0x8c, 0xa9, 0x36, 0x3f, 0xf2, 0x85, 0x54, 0x4e, 0x92, 0xd8, 0x85, 0x01, 0x46, 0xd6, 0x50}}},
@@ -1186,7 +1047,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x93, 0x1c, 0x06, 0x2d, 0x66, 0x65, 0x02, 0xa4, 0x97, 0x18, 0xfd, 0x00, 0xe7, 0xab, 0x03, 0xec, 0xce, 0xc1, 0xbf, 0x37, 0xf8, 0x13, 0x53, 0xa5, 0xe5, 0x0c, 0x3a, 0xa8, 0x55, 0xb9, 0xff, 0x68}}},
{{{0xe4, 0xe6, 0x6d, 0x30, 0x7d, 0x30, 0x35, 0xc2, 0x78, 0x87, 0xf9, 0xfc, 0x6b, 0x5a, 0xc3, 0xb7, 0x65, 0xd8, 0x2e, 0xc7, 0xa5, 0x0c, 0xc6, 0xdc, 0x12, 0xaa, 0xd6, 0x4f, 0xc5, 0x38, 0xbc, 0x0e}} ,
{{0xe2, 0x3c, 0x76, 0x86, 0x38, 0xf2, 0x7b, 0x2c, 0x16, 0x78, 0x8d, 0xf5, 0xa4, 0x15, 0xda, 0xdb, 0x26, 0x85, 0xa0, 0x56, 0xdd, 0x1d, 0xe3, 0xb3, 0xfd, 0x40, 0xef, 0xf2, 0xd9, 0xa1, 0xb3, 0x04}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xdb, 0x49, 0x0e, 0xe6, 0x58, 0x10, 0x7a, 0x52, 0xda, 0xb5, 0x7d, 0x37, 0x6a, 0x3e, 0xa1, 0x78, 0xce, 0xc7, 0x1c, 0x24, 0x23, 0xdb, 0x7d, 0xfb, 0x8c, 0x8d, 0xdc, 0x30, 0x67, 0x69, 0x75, 0x3b}} ,
{{0xa9, 0xea, 0x6d, 0x16, 0x16, 0x60, 0xf4, 0x60, 0x87, 0x19, 0x44, 0x8c, 0x4a, 0x8b, 0x3e, 0xfb, 0x16, 0x00, 0x00, 0x54, 0xa6, 0x9e, 0x9f, 0xef, 0xcf, 0xd9, 0xd2, 0x4c, 0x74, 0x31, 0xd0, 0x34}}},
@@ -1196,7 +1057,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xb0, 0x8b, 0xaa, 0x1e, 0xec, 0xc7, 0xa5, 0x8f, 0x1f, 0x92, 0x04, 0xc6, 0x05, 0xf6, 0xdf, 0xa1, 0xcc, 0x1f, 0x81, 0xf5, 0x0e, 0x9c, 0x57, 0xdc, 0xe3, 0xbb, 0x06, 0x87, 0x1e, 0xfe, 0x23, 0x6c}}},
{{{0xd8, 0x2b, 0x5b, 0x16, 0xea, 0x20, 0xf1, 0xd3, 0x68, 0x8f, 0xae, 0x5b, 0xd0, 0xa9, 0x1a, 0x19, 0xa8, 0x36, 0xfb, 0x2b, 0x57, 0x88, 0x7d, 0x90, 0xd5, 0xa6, 0xf3, 0xdc, 0x38, 0x89, 0x4e, 0x1f}} ,
{{0xcc, 0x19, 0xda, 0x9b, 0x3b, 0x43, 0x48, 0x21, 0x2e, 0x23, 0x4d, 0x3d, 0xae, 0xf8, 0x8c, 0xfc, 0xdd, 0xa6, 0x74, 0x37, 0x65, 0xca, 0xee, 0x1a, 0x19, 0x8e, 0x9f, 0x64, 0x6f, 0x0c, 0x8b, 0x5a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x25, 0xb9, 0xc2, 0xf0, 0x72, 0xb8, 0x15, 0x16, 0xcc, 0x8d, 0x3c, 0x6f, 0x25, 0xed, 0xf4, 0x46, 0x2e, 0x0c, 0x60, 0x0f, 0xe2, 0x84, 0x34, 0x55, 0x89, 0x59, 0x34, 0x1b, 0xf5, 0x8d, 0xfe, 0x08}} ,
{{0xf8, 0xab, 0x93, 0xbc, 0x44, 0xba, 0x1b, 0x75, 0x4b, 0x49, 0x6f, 0xd0, 0x54, 0x2e, 0x63, 0xba, 0xb5, 0xea, 0xed, 0x32, 0x14, 0xc9, 0x94, 0xd8, 0xc5, 0xce, 0xf4, 0x10, 0x68, 0xe0, 0x38, 0x27}}},
@@ -1206,7 +1067,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x6c, 0xba, 0x14, 0xc5, 0xea, 0x12, 0x9e, 0x2e, 0x82, 0x63, 0xce, 0x9b, 0x4a, 0xe7, 0x1d, 0xec, 0xf1, 0x2e, 0x51, 0x1c, 0xf4, 0xd0, 0x69, 0x15, 0x42, 0x9d, 0xa3, 0x3f, 0x0e, 0xbf, 0xe9, 0x5c}}},
{{{0xe4, 0x0d, 0xf4, 0xbd, 0xee, 0x31, 0x10, 0xed, 0xcb, 0x12, 0x86, 0xad, 0xd4, 0x2f, 0x90, 0x37, 0x32, 0xc3, 0x0b, 0x73, 0xec, 0x97, 0x85, 0xa4, 0x01, 0x1c, 0x76, 0x35, 0xfe, 0x75, 0xdd, 0x71}} ,
{{0x11, 0xa4, 0x88, 0x9f, 0x3e, 0x53, 0x69, 0x3b, 0x1b, 0xe0, 0xf7, 0xba, 0x9b, 0xad, 0x4e, 0x81, 0x5f, 0xb5, 0x5c, 0xae, 0xbe, 0x67, 0x86, 0x37, 0x34, 0x8e, 0x07, 0x32, 0x45, 0x4a, 0x67, 0x39}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x90, 0x70, 0x58, 0x20, 0x03, 0x1e, 0x67, 0xb2, 0xc8, 0x9b, 0x58, 0xc5, 0xb1, 0xeb, 0x2d, 0x4a, 0xde, 0x82, 0x8c, 0xf2, 0xd2, 0x14, 0xb8, 0x70, 0x61, 0x4e, 0x73, 0xd6, 0x0b, 0x6b, 0x0d, 0x30}} ,
{{0x81, 0xfc, 0x55, 0x5c, 0xbf, 0xa7, 0xc4, 0xbd, 0xe2, 0xf0, 0x4b, 0x8f, 0xe9, 0x7d, 0x99, 0xfa, 0xd3, 0xab, 0xbc, 0xc7, 0x83, 0x2b, 0x04, 0x7f, 0x0c, 0x19, 0x43, 0x03, 0x3d, 0x07, 0xca, 0x40}}},
@@ -1216,7 +1077,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x17, 0x62, 0x6b, 0x14, 0xa1, 0x7c, 0xd0, 0x79, 0x6e, 0xd8, 0x8a, 0xa5, 0x6d, 0x8c, 0x93, 0xd2, 0x3f, 0xec, 0x44, 0x8d, 0x6e, 0x91, 0x01, 0x8c, 0x8f, 0xee, 0x01, 0x8f, 0xc0, 0xb4, 0x85, 0x0e}}},
{{{0x02, 0x3a, 0x70, 0x41, 0xe4, 0x11, 0x57, 0x23, 0xac, 0xe6, 0xfc, 0x54, 0x7e, 0xcd, 0xd7, 0x22, 0xcb, 0x76, 0x9f, 0x20, 0xce, 0xa0, 0x73, 0x76, 0x51, 0x3b, 0xa4, 0xf8, 0xe3, 0x62, 0x12, 0x6c}} ,
{{0x7f, 0x00, 0x9c, 0x26, 0x0d, 0x6f, 0x48, 0x7f, 0x3a, 0x01, 0xed, 0xc5, 0x96, 0xb0, 0x1f, 0x4f, 0xa8, 0x02, 0x62, 0x27, 0x8a, 0x50, 0x8d, 0x9a, 0x8b, 0x52, 0x0f, 0x1e, 0xcf, 0x41, 0x38, 0x19}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xf5, 0x6c, 0xd4, 0x2f, 0x0f, 0x69, 0x0f, 0x87, 0x3f, 0x61, 0x65, 0x1e, 0x35, 0x34, 0x85, 0xba, 0x02, 0x30, 0xac, 0x25, 0x3d, 0xe2, 0x62, 0xf1, 0xcc, 0xe9, 0x1b, 0xc2, 0xef, 0x6a, 0x42, 0x57}} ,
{{0x34, 0x1f, 0x2e, 0xac, 0xd1, 0xc7, 0x04, 0x52, 0x32, 0x66, 0xb2, 0x33, 0x73, 0x21, 0x34, 0x54, 0xf7, 0x71, 0xed, 0x06, 0xb0, 0xff, 0xa6, 0x59, 0x6f, 0x8a, 0x4e, 0xfb, 0x02, 0xb0, 0x45, 0x6b}}},
@@ -1226,7 +1087,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xdd, 0xb5, 0x7d, 0xab, 0x5a, 0x21, 0x41, 0x53, 0xbb, 0x17, 0x79, 0x0d, 0xd1, 0xa8, 0x0c, 0x0c, 0x20, 0x88, 0x09, 0xe9, 0x84, 0xe8, 0x25, 0x11, 0x67, 0x7a, 0x8b, 0x1a, 0xe4, 0x5d, 0xe1, 0x5d}}},
{{{0x37, 0xea, 0xfe, 0x65, 0x3b, 0x25, 0xe8, 0xe1, 0xc2, 0xc5, 0x02, 0xa4, 0xbe, 0x98, 0x0a, 0x2b, 0x61, 0xc1, 0x9b, 0xe2, 0xd5, 0x92, 0xe6, 0x9e, 0x7d, 0x1f, 0xca, 0x43, 0x88, 0x8b, 0x2c, 0x59}} ,
{{0xe0, 0xb5, 0x00, 0x1d, 0x2a, 0x6f, 0xaf, 0x79, 0x86, 0x2f, 0xa6, 0x5a, 0x93, 0xd1, 0xfe, 0xae, 0x3a, 0xee, 0xdb, 0x7c, 0x61, 0xbe, 0x7c, 0x01, 0xf9, 0xfe, 0x52, 0xdc, 0xd8, 0x52, 0xa3, 0x42}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x22, 0xaf, 0x13, 0x37, 0xbd, 0x37, 0x71, 0xac, 0x04, 0x46, 0x63, 0xac, 0xa4, 0x77, 0xed, 0x25, 0x38, 0xe0, 0x15, 0xa8, 0x64, 0x00, 0x0d, 0xce, 0x51, 0x01, 0xa9, 0xbc, 0x0f, 0x03, 0x1c, 0x04}} ,
{{0x89, 0xf9, 0x80, 0x07, 0xcf, 0x3f, 0xb3, 0xe9, 0xe7, 0x45, 0x44, 0x3d, 0x2a, 0x7c, 0xe9, 0xe4, 0x16, 0x5c, 0x5e, 0x65, 0x1c, 0xc7, 0x7d, 0xc6, 0x7a, 0xfb, 0x43, 0xee, 0x25, 0x76, 0x46, 0x72}}},
@@ -1236,7 +1097,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x0d, 0xac, 0x66, 0x6d, 0xc3, 0x8b, 0xba, 0x16, 0xb5, 0xe2, 0xa0, 0x0d, 0x0c, 0xbd, 0xa4, 0x8e, 0x18, 0x6c, 0xf2, 0xdc, 0xf9, 0xdc, 0x4a, 0x86, 0x25, 0x95, 0x14, 0xcb, 0xd8, 0x1a, 0x04, 0x0f}}},
{{{0x97, 0xa5, 0xdb, 0x8b, 0x2d, 0xaa, 0x42, 0x11, 0x09, 0xf2, 0x93, 0xbb, 0xd9, 0x06, 0x84, 0x4e, 0x11, 0xa8, 0xa0, 0x25, 0x2b, 0xa6, 0x5f, 0xae, 0xc4, 0xb4, 0x4c, 0xc8, 0xab, 0xc7, 0x3b, 0x02}} ,
{{0xee, 0xc9, 0x29, 0x0f, 0xdf, 0x11, 0x85, 0xed, 0xce, 0x0d, 0x62, 0x2c, 0x8f, 0x4b, 0xf9, 0x04, 0xe9, 0x06, 0x72, 0x1d, 0x37, 0x20, 0x50, 0xc9, 0x14, 0xeb, 0xec, 0x39, 0xa7, 0x97, 0x2b, 0x4d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x69, 0xd1, 0x39, 0xbd, 0xfb, 0x33, 0xbe, 0xc4, 0xf0, 0x5c, 0xef, 0xf0, 0x56, 0x68, 0xfc, 0x97, 0x47, 0xc8, 0x72, 0xb6, 0x53, 0xa4, 0x0a, 0x98, 0xa5, 0xb4, 0x37, 0x71, 0xcf, 0x66, 0x50, 0x6d}} ,
{{0x17, 0xa4, 0x19, 0x52, 0x11, 0x47, 0xb3, 0x5c, 0x5b, 0xa9, 0x2e, 0x22, 0xb4, 0x00, 0x52, 0xf9, 0x57, 0x18, 0xb8, 0xbe, 0x5a, 0xe3, 0xab, 0x83, 0xc8, 0x87, 0x0a, 0x2a, 0xd8, 0x8c, 0xbb, 0x54}}},
@@ -1246,7 +1107,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x20, 0xd6, 0xd8, 0x91, 0x54, 0xa7, 0xf3, 0x20, 0x4b, 0x34, 0x06, 0xfa, 0x30, 0xc8, 0x6f, 0x14, 0x10, 0x65, 0x74, 0x13, 0x4e, 0xf0, 0x69, 0x26, 0xce, 0xcf, 0x90, 0xf4, 0xd0, 0xc5, 0xc8, 0x64}}},
{{{0x26, 0xa2, 0x50, 0x02, 0x24, 0x72, 0xf1, 0xf0, 0x4e, 0x2d, 0x93, 0xd5, 0x08, 0xe7, 0xae, 0x38, 0xf7, 0x18, 0xa5, 0x32, 0x34, 0xc2, 0xf0, 0xa6, 0xec, 0xb9, 0x61, 0x7b, 0x64, 0x99, 0xac, 0x71}} ,
{{0x25, 0xcf, 0x74, 0x55, 0x1b, 0xaa, 0xa9, 0x38, 0x41, 0x40, 0xd5, 0x95, 0x95, 0xab, 0x1c, 0x5e, 0xbc, 0x41, 0x7e, 0x14, 0x30, 0xbe, 0x13, 0x89, 0xf4, 0xe5, 0xeb, 0x28, 0xc0, 0xc2, 0x96, 0x3a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x2b, 0x77, 0x45, 0xec, 0x67, 0x76, 0x32, 0x4c, 0xb9, 0xdf, 0x25, 0x32, 0x6b, 0xcb, 0xe7, 0x14, 0x61, 0x43, 0xee, 0xba, 0x9b, 0x71, 0xef, 0xd2, 0x48, 0x65, 0xbb, 0x1b, 0x8a, 0x13, 0x1b, 0x22}} ,
{{0x84, 0xad, 0x0c, 0x18, 0x38, 0x5a, 0xba, 0xd0, 0x98, 0x59, 0xbf, 0x37, 0xb0, 0x4f, 0x97, 0x60, 0x20, 0xb3, 0x9b, 0x97, 0xf6, 0x08, 0x6c, 0xa4, 0xff, 0xfb, 0xb7, 0xfa, 0x95, 0xb2, 0x51, 0x79}}},
@@ -1256,7 +1117,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xa9, 0x85, 0xf4, 0xe7, 0xb0, 0x15, 0x33, 0x84, 0x1b, 0x14, 0x1a, 0x02, 0xd9, 0x3b, 0xad, 0x0f, 0x43, 0x6c, 0xea, 0x3e, 0x0f, 0x7e, 0xda, 0xdd, 0x6b, 0x4c, 0x7f, 0x6e, 0xd4, 0x6b, 0xbf, 0x0f}}},
{{{0x47, 0x9f, 0x7c, 0x56, 0x7c, 0x43, 0x91, 0x1c, 0xbb, 0x4e, 0x72, 0x3e, 0x64, 0xab, 0xa0, 0xa0, 0xdf, 0xb4, 0xd8, 0x87, 0x3a, 0xbd, 0xa8, 0x48, 0xc9, 0xb8, 0xef, 0x2e, 0xad, 0x6f, 0x84, 0x4f}} ,
{{0x2d, 0x2d, 0xf0, 0x1b, 0x7e, 0x2a, 0x6c, 0xf8, 0xa9, 0x6a, 0xe1, 0xf0, 0x99, 0xa1, 0x67, 0x9a, 0xd4, 0x13, 0xca, 0xca, 0xba, 0x27, 0x92, 0xaa, 0xa1, 0x5d, 0x50, 0xde, 0xcc, 0x40, 0x26, 0x0a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x9f, 0x3e, 0xf2, 0xb2, 0x90, 0xce, 0xdb, 0x64, 0x3e, 0x03, 0xdd, 0x37, 0x36, 0x54, 0x70, 0x76, 0x24, 0xb5, 0x69, 0x03, 0xfc, 0xa0, 0x2b, 0x74, 0xb2, 0x05, 0x0e, 0xcc, 0xd8, 0x1f, 0x6a, 0x1f}} ,
{{0x19, 0x5e, 0x60, 0x69, 0x58, 0x86, 0xa0, 0x31, 0xbd, 0x32, 0xe9, 0x2c, 0x5c, 0xd2, 0x85, 0xba, 0x40, 0x64, 0xa8, 0x74, 0xf8, 0x0e, 0x1c, 0xb3, 0xa9, 0x69, 0xe8, 0x1e, 0x40, 0x64, 0x99, 0x77}}},
@@ -1266,7 +1127,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xca, 0x59, 0x74, 0x1a, 0x11, 0xef, 0x6d, 0xf7, 0x39, 0x5c, 0x3b, 0x1f, 0xfa, 0xe3, 0x40, 0x41, 0x23, 0x9e, 0xf6, 0xd1, 0x21, 0xa2, 0xbf, 0xad, 0x65, 0x42, 0x6b, 0x59, 0x8a, 0xe8, 0xc5, 0x7f}}},
{{{0x64, 0x05, 0x7a, 0x84, 0x4a, 0x13, 0xc3, 0xf6, 0xb0, 0x6e, 0x9a, 0x6b, 0x53, 0x6b, 0x32, 0xda, 0xd9, 0x74, 0x75, 0xc4, 0xba, 0x64, 0x3d, 0x3b, 0x08, 0xdd, 0x10, 0x46, 0xef, 0xc7, 0x90, 0x1f}} ,
{{0x7b, 0x2f, 0x3a, 0xce, 0xc8, 0xa1, 0x79, 0x3c, 0x30, 0x12, 0x44, 0x28, 0xf6, 0xbc, 0xff, 0xfd, 0xf4, 0xc0, 0x97, 0xb0, 0xcc, 0xc3, 0x13, 0x7a, 0xb9, 0x9a, 0x16, 0xe4, 0xcb, 0x4c, 0x34, 0x63}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x07, 0x4e, 0xd3, 0x2d, 0x09, 0x33, 0x0e, 0xd2, 0x0d, 0xbe, 0x3e, 0xe7, 0xe4, 0xaa, 0xb7, 0x00, 0x8b, 0xe8, 0xad, 0xaa, 0x7a, 0x8d, 0x34, 0x28, 0xa9, 0x81, 0x94, 0xc5, 0xe7, 0x42, 0xac, 0x47}} ,
{{0x24, 0x89, 0x7a, 0x8f, 0xb5, 0x9b, 0xf0, 0xc2, 0x03, 0x64, 0xd0, 0x1e, 0xf5, 0xa4, 0xb2, 0xf3, 0x74, 0xe9, 0x1a, 0x16, 0xfd, 0xcb, 0x15, 0xea, 0xeb, 0x10, 0x6c, 0x35, 0xd1, 0xc1, 0xa6, 0x28}}},
@@ -1276,7 +1137,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x19, 0xa6, 0x5a, 0x65, 0x93, 0xc3, 0xb5, 0x31, 0x22, 0x4f, 0xf3, 0xf6, 0x0f, 0xeb, 0x28, 0xc3, 0x7c, 0xeb, 0xce, 0x86, 0xec, 0x67, 0x76, 0x6e, 0x35, 0x45, 0x7b, 0xd8, 0x6b, 0x92, 0x01, 0x65}}},
{{{0x3d, 0xd5, 0x9a, 0x64, 0x73, 0x36, 0xb1, 0xd6, 0x86, 0x98, 0x42, 0x3f, 0x8a, 0xf1, 0xc7, 0xf5, 0x42, 0xa8, 0x9c, 0x52, 0xa8, 0xdc, 0xf9, 0x24, 0x3f, 0x4a, 0xa1, 0xa4, 0x5b, 0xe8, 0x62, 0x1a}} ,
{{0xc5, 0xbd, 0xc8, 0x14, 0xd5, 0x0d, 0xeb, 0xe1, 0xa5, 0xe6, 0x83, 0x11, 0x09, 0x00, 0x1d, 0x55, 0x83, 0x51, 0x7e, 0x75, 0x00, 0x81, 0xb9, 0xcb, 0xd8, 0xc5, 0xe5, 0xa1, 0xd9, 0x17, 0x6d, 0x1f}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xea, 0xf9, 0xe4, 0xe9, 0xe1, 0x52, 0x3f, 0x51, 0x19, 0x0d, 0xdd, 0xd9, 0x9d, 0x93, 0x31, 0x87, 0x23, 0x09, 0xd5, 0x83, 0xeb, 0x92, 0x09, 0x76, 0x6e, 0xe3, 0xf8, 0xc0, 0xa2, 0x66, 0xb5, 0x36}} ,
{{0x3a, 0xbb, 0x39, 0xed, 0x32, 0x02, 0xe7, 0x43, 0x7a, 0x38, 0x14, 0x84, 0xe3, 0x44, 0xd2, 0x5e, 0x94, 0xdd, 0x78, 0x89, 0x55, 0x4c, 0x73, 0x9e, 0xe1, 0xe4, 0x3e, 0x43, 0xd0, 0x4a, 0xde, 0x1b}}},
@@ -1286,7 +1147,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x46, 0xda, 0x9e, 0x51, 0x3a, 0xe6, 0xd1, 0xa6, 0xbb, 0x4d, 0x7b, 0x08, 0xbe, 0x8c, 0xd5, 0xf3, 0x3f, 0xfd, 0xf7, 0x44, 0x80, 0x2d, 0x53, 0x4b, 0xd0, 0x87, 0x68, 0xc1, 0xb5, 0xd8, 0xf7, 0x07}}},
{{{0xf4, 0x10, 0x46, 0xbe, 0xb7, 0xd2, 0xd1, 0xce, 0x5e, 0x76, 0xa2, 0xd7, 0x03, 0xdc, 0xe4, 0x81, 0x5a, 0xf6, 0x3c, 0xde, 0xae, 0x7a, 0x9d, 0x21, 0x34, 0xa5, 0xf6, 0xa9, 0x73, 0xe2, 0x8d, 0x60}} ,
{{0xfa, 0x44, 0x71, 0xf6, 0x41, 0xd8, 0xc6, 0x58, 0x13, 0x37, 0xeb, 0x84, 0x0f, 0x96, 0xc7, 0xdc, 0xc8, 0xa9, 0x7a, 0x83, 0xb2, 0x2f, 0x31, 0xb1, 0x1a, 0xd8, 0x98, 0x3f, 0x11, 0xd0, 0x31, 0x3b}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x81, 0xd5, 0x34, 0x16, 0x01, 0xa3, 0x93, 0xea, 0x52, 0x94, 0xec, 0x93, 0xb7, 0x81, 0x11, 0x2d, 0x58, 0xf9, 0xb5, 0x0a, 0xaa, 0x4f, 0xf6, 0x2e, 0x3f, 0x36, 0xbf, 0x33, 0x5a, 0xe7, 0xd1, 0x08}} ,
{{0x1a, 0xcf, 0x42, 0xae, 0xcc, 0xb5, 0x77, 0x39, 0xc4, 0x5b, 0x5b, 0xd0, 0x26, 0x59, 0x27, 0xd0, 0x55, 0x71, 0x12, 0x9d, 0x88, 0x3d, 0x9c, 0xea, 0x41, 0x6a, 0xf0, 0x50, 0x93, 0x93, 0xdd, 0x47}}},
@@ -1296,7 +1157,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x13, 0xa8, 0x82, 0x3e, 0xe9, 0x13, 0xad, 0xeb, 0x01, 0xca, 0xcf, 0xda, 0xcd, 0xf7, 0x6c, 0xc7, 0x7a, 0xdc, 0x1e, 0x6e, 0xc8, 0x4e, 0x55, 0x62, 0x80, 0xea, 0x78, 0x0c, 0x86, 0xb9, 0x40, 0x51}}},
{{{0x27, 0xae, 0xd3, 0x0d, 0x4c, 0x8f, 0x34, 0xea, 0x7d, 0x3c, 0xe5, 0x8a, 0xcf, 0x5b, 0x92, 0xd8, 0x30, 0x16, 0xb4, 0xa3, 0x75, 0xff, 0xeb, 0x27, 0xc8, 0x5c, 0x6c, 0xc2, 0xee, 0x6c, 0x21, 0x0b}} ,
{{0xc3, 0xba, 0x12, 0x53, 0x2a, 0xaa, 0x77, 0xad, 0x19, 0x78, 0x55, 0x8a, 0x2e, 0x60, 0x87, 0xc2, 0x6e, 0x91, 0x38, 0x91, 0x3f, 0x7a, 0xc5, 0x24, 0x8f, 0x51, 0xc5, 0xde, 0xb0, 0x53, 0x30, 0x56}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x02, 0xfe, 0x54, 0x12, 0x18, 0xca, 0x7d, 0xa5, 0x68, 0x43, 0xa3, 0x6d, 0x14, 0x2a, 0x6a, 0xa5, 0x8e, 0x32, 0xe7, 0x63, 0x4f, 0xe3, 0xc6, 0x44, 0x3e, 0xab, 0x63, 0xca, 0x17, 0x86, 0x74, 0x3f}} ,
{{0x1e, 0x64, 0xc1, 0x7d, 0x52, 0xdc, 0x13, 0x5a, 0xa1, 0x9c, 0x4e, 0xee, 0x99, 0x28, 0xbb, 0x4c, 0xee, 0xac, 0xa9, 0x1b, 0x89, 0xa2, 0x38, 0x39, 0x7b, 0xc4, 0x0f, 0x42, 0xe6, 0x89, 0xed, 0x0f}}},
@@ -1306,7 +1167,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xe7, 0x29, 0xf9, 0x37, 0xd9, 0x46, 0x5a, 0xcd, 0x70, 0xfe, 0x4d, 0x5b, 0xbf, 0xa5, 0xcf, 0x91, 0xf4, 0xef, 0xee, 0x8a, 0x29, 0xd0, 0xe7, 0xc4, 0x25, 0x92, 0x8a, 0xff, 0x36, 0xfc, 0xe4, 0x49}}},
{{{0xbd, 0x00, 0xb9, 0x04, 0x7d, 0x35, 0xfc, 0xeb, 0xd0, 0x0b, 0x05, 0x32, 0x52, 0x7a, 0x89, 0x24, 0x75, 0x50, 0xe1, 0x63, 0x02, 0x82, 0x8e, 0xe7, 0x85, 0x0c, 0xf2, 0x56, 0x44, 0x37, 0x83, 0x25}} ,
{{0x8f, 0xa1, 0xce, 0xcb, 0x60, 0xda, 0x12, 0x02, 0x1e, 0x29, 0x39, 0x2a, 0x03, 0xb7, 0xeb, 0x77, 0x40, 0xea, 0xc9, 0x2b, 0x2c, 0xd5, 0x7d, 0x7e, 0x2c, 0xc7, 0x5a, 0xfd, 0xff, 0xc4, 0xd1, 0x62}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x1d, 0x88, 0x98, 0x5b, 0x4e, 0xfc, 0x41, 0x24, 0x05, 0xe6, 0x50, 0x2b, 0xae, 0x96, 0x51, 0xd9, 0x6b, 0x72, 0xb2, 0x33, 0x42, 0x98, 0x68, 0xbb, 0x10, 0x5a, 0x7a, 0x8c, 0x9d, 0x07, 0xb4, 0x05}} ,
{{0x2f, 0x61, 0x9f, 0xd7, 0xa8, 0x3f, 0x83, 0x8c, 0x10, 0x69, 0x90, 0xe6, 0xcf, 0xd2, 0x63, 0xa3, 0xe4, 0x54, 0x7e, 0xe5, 0x69, 0x13, 0x1c, 0x90, 0x57, 0xaa, 0xe9, 0x53, 0x22, 0x43, 0x29, 0x23}}},
@@ -1316,7 +1177,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xf5, 0xff, 0xcc, 0xf0, 0xb4, 0x12, 0x03, 0x5f, 0xc9, 0x84, 0xcb, 0x1d, 0x17, 0xe0, 0xbc, 0xcc, 0x03, 0x62, 0xa9, 0x8b, 0x94, 0xa6, 0xaa, 0x18, 0xcb, 0x27, 0x8d, 0x49, 0xa6, 0x17, 0x15, 0x07}}},
{{{0xd9, 0xb6, 0xd4, 0x9d, 0xd4, 0x6a, 0xaf, 0x70, 0x07, 0x2c, 0x10, 0x9e, 0xbd, 0x11, 0xad, 0xe4, 0x26, 0x33, 0x70, 0x92, 0x78, 0x1c, 0x74, 0x9f, 0x75, 0x60, 0x56, 0xf4, 0x39, 0xa8, 0xa8, 0x62}} ,
{{0x3b, 0xbf, 0x55, 0x35, 0x61, 0x8b, 0x44, 0x97, 0xe8, 0x3a, 0x55, 0xc1, 0xc8, 0x3b, 0xfd, 0x95, 0x29, 0x11, 0x60, 0x96, 0x1e, 0xcb, 0x11, 0x9d, 0xc2, 0x03, 0x8a, 0x1b, 0xc6, 0xd6, 0x45, 0x3d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x7e, 0x0e, 0x50, 0xb2, 0xcc, 0x0d, 0x6b, 0xa6, 0x71, 0x5b, 0x42, 0xed, 0xbd, 0xaf, 0xac, 0xf0, 0xfc, 0x12, 0xa2, 0x3f, 0x4e, 0xda, 0xe8, 0x11, 0xf3, 0x23, 0xe1, 0x04, 0x62, 0x03, 0x1c, 0x4e}} ,
{{0xc8, 0xb1, 0x1b, 0x6f, 0x73, 0x61, 0x3d, 0x27, 0x0d, 0x7d, 0x7a, 0x25, 0x5f, 0x73, 0x0e, 0x2f, 0x93, 0xf6, 0x24, 0xd8, 0x4f, 0x90, 0xac, 0xa2, 0x62, 0x0a, 0xf0, 0x61, 0xd9, 0x08, 0x59, 0x6a}}},
@@ -1326,7 +1187,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x8d, 0xa6, 0xd5, 0xac, 0x9d, 0xcc, 0x68, 0x75, 0x7f, 0xc3, 0x4d, 0x4b, 0xdd, 0x6c, 0xbb, 0x11, 0x5a, 0x60, 0xe5, 0xbd, 0x7d, 0x27, 0x8b, 0xda, 0xb4, 0x95, 0xf6, 0x03, 0x27, 0xa4, 0x92, 0x3f}}},
{{{0x22, 0xd6, 0xb5, 0x17, 0x84, 0xbf, 0x12, 0xcc, 0x23, 0x14, 0x4a, 0xdf, 0x14, 0x31, 0xbc, 0xa1, 0xac, 0x6e, 0xab, 0xfa, 0x57, 0x11, 0x53, 0xb3, 0x27, 0xe6, 0xf9, 0x47, 0x33, 0x44, 0x34, 0x1e}} ,
{{0x79, 0xfc, 0xa6, 0xb4, 0x0b, 0x35, 0x20, 0xc9, 0x4d, 0x22, 0x84, 0xc4, 0xa9, 0x20, 0xec, 0x89, 0x94, 0xba, 0x66, 0x56, 0x48, 0xb9, 0x87, 0x7f, 0xca, 0x1e, 0x06, 0xed, 0xa5, 0x55, 0x59, 0x29}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x56, 0xe1, 0xf5, 0xf1, 0xd5, 0xab, 0xa8, 0x2b, 0xae, 0x89, 0xf3, 0xcf, 0x56, 0x9f, 0xf2, 0x4b, 0x31, 0xbc, 0x18, 0xa9, 0x06, 0x5b, 0xbe, 0xb4, 0x61, 0xf8, 0xb2, 0x06, 0x9c, 0x81, 0xab, 0x4c}} ,
{{0x1f, 0x68, 0x76, 0x01, 0x16, 0x38, 0x2b, 0x0f, 0x77, 0x97, 0x92, 0x67, 0x4e, 0x86, 0x6a, 0x8b, 0xe5, 0xe8, 0x0c, 0xf7, 0x36, 0x39, 0xb5, 0x33, 0xe6, 0xcf, 0x5e, 0xbd, 0x18, 0xfb, 0x10, 0x1f}}},
@@ -1336,7 +1197,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xf5, 0x1a, 0x61, 0xf7, 0x37, 0x9d, 0x00, 0xf4, 0xf2, 0x69, 0x6f, 0x4b, 0x01, 0x85, 0x19, 0x45, 0x4d, 0x7f, 0x02, 0x7c, 0x6a, 0x05, 0x47, 0x6c, 0x1f, 0x81, 0x20, 0xd4, 0xe8, 0x50, 0x27, 0x72}}},
{{{0x2c, 0x3a, 0xe5, 0xad, 0xf4, 0xdd, 0x2d, 0xf7, 0x5c, 0x44, 0xb5, 0x5b, 0x21, 0xa3, 0x89, 0x5f, 0x96, 0x45, 0xca, 0x4d, 0xa4, 0x21, 0x99, 0x70, 0xda, 0xc4, 0xc4, 0xa0, 0xe5, 0xf4, 0xec, 0x0a}} ,
{{0x07, 0x68, 0x21, 0x65, 0xe9, 0x08, 0xa0, 0x0b, 0x6a, 0x4a, 0xba, 0xb5, 0x80, 0xaf, 0xd0, 0x1b, 0xc5, 0xf5, 0x4b, 0x73, 0x50, 0x60, 0x2d, 0x71, 0x69, 0x61, 0x0e, 0xc0, 0x20, 0x40, 0x30, 0x19}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xd0, 0x75, 0x57, 0x3b, 0xeb, 0x5c, 0x14, 0x56, 0x50, 0xc9, 0x4f, 0xb8, 0xb8, 0x1e, 0xa3, 0xf4, 0xab, 0xf5, 0xa9, 0x20, 0x15, 0x94, 0x82, 0xda, 0x96, 0x1c, 0x9b, 0x59, 0x8c, 0xff, 0xf4, 0x51}} ,
{{0xc1, 0x3a, 0x86, 0xd7, 0xb0, 0x06, 0x84, 0x7f, 0x1b, 0xbd, 0xd4, 0x07, 0x78, 0x80, 0x2e, 0xb1, 0xb4, 0xee, 0x52, 0x38, 0xee, 0x9a, 0xf9, 0xf6, 0xf3, 0x41, 0x6e, 0xd4, 0x88, 0x95, 0xac, 0x35}}},
@@ -1346,7 +1207,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x34, 0xe9, 0x59, 0x93, 0x9d, 0x26, 0x80, 0x54, 0xf2, 0xcc, 0x3c, 0xc2, 0x25, 0x85, 0xe3, 0x6a, 0xc1, 0x62, 0x04, 0xa7, 0x08, 0x32, 0x6d, 0xa1, 0x39, 0x84, 0x8a, 0x3b, 0x87, 0x5f, 0x11, 0x13}}},
{{{0xda, 0x03, 0x34, 0x66, 0xc4, 0x0c, 0x73, 0x6e, 0xbc, 0x24, 0xb5, 0xf9, 0x70, 0x81, 0x52, 0xe9, 0xf4, 0x7c, 0x23, 0xdd, 0x9f, 0xb8, 0x46, 0xef, 0x1d, 0x22, 0x55, 0x7d, 0x71, 0xc4, 0x42, 0x33}} ,
{{0xc5, 0x37, 0x69, 0x5b, 0xa8, 0xc6, 0x9d, 0xa4, 0xfc, 0x61, 0x6e, 0x68, 0x46, 0xea, 0xd7, 0x1c, 0x67, 0xd2, 0x7d, 0xfa, 0xf1, 0xcc, 0x54, 0x8d, 0x36, 0x35, 0xc9, 0x00, 0xdf, 0x6c, 0x67, 0x50}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x9a, 0x4d, 0x42, 0x29, 0x5d, 0xa4, 0x6b, 0x6f, 0xa8, 0x8a, 0x4d, 0x91, 0x7b, 0xd2, 0xdf, 0x36, 0xef, 0x01, 0x22, 0xc5, 0xcc, 0x8d, 0xeb, 0x58, 0x3d, 0xb3, 0x50, 0xfc, 0x8b, 0x97, 0x96, 0x33}} ,
{{0x93, 0x33, 0x07, 0xc8, 0x4a, 0xca, 0xd0, 0xb1, 0xab, 0xbd, 0xdd, 0xa7, 0x7c, 0xac, 0x3e, 0x45, 0xcb, 0xcc, 0x07, 0x91, 0xbf, 0x35, 0x9d, 0xcb, 0x7d, 0x12, 0x3c, 0x11, 0x59, 0x13, 0xcf, 0x5c}}},
@@ -1356,7 +1217,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x8b, 0xa4, 0xd6, 0x50, 0xb4, 0xaa, 0x5d, 0x64, 0x64, 0x76, 0x2e, 0xa1, 0xa6, 0xb3, 0xb8, 0x7c, 0x7a, 0x56, 0xf5, 0x5c, 0x4e, 0x84, 0x5c, 0xfb, 0xdd, 0xca, 0x48, 0x8b, 0x48, 0xb9, 0xba, 0x34}}},
{{{0xc5, 0xe3, 0xe8, 0xae, 0x17, 0x27, 0xe3, 0x64, 0x60, 0x71, 0x47, 0x29, 0x02, 0x0f, 0x92, 0x5d, 0x10, 0x93, 0xc8, 0x0e, 0xa1, 0xed, 0xba, 0xa9, 0x96, 0x1c, 0xc5, 0x76, 0x30, 0xcd, 0xf9, 0x30}} ,
{{0x95, 0xb0, 0xbd, 0x8c, 0xbc, 0xa7, 0x4f, 0x7e, 0xfd, 0x4e, 0x3a, 0xbf, 0x5f, 0x04, 0x79, 0x80, 0x2b, 0x5a, 0x9f, 0x4f, 0x68, 0x21, 0x19, 0x71, 0xc6, 0x20, 0x01, 0x42, 0xaa, 0xdf, 0xae, 0x2c}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x90, 0x6e, 0x7e, 0x4b, 0x71, 0x93, 0xc0, 0x72, 0xed, 0xeb, 0x71, 0x24, 0x97, 0x26, 0x9c, 0xfe, 0xcb, 0x3e, 0x59, 0x19, 0xa8, 0x0f, 0x75, 0x7d, 0xbe, 0x18, 0xe6, 0x96, 0x1e, 0x95, 0x70, 0x60}} ,
{{0x89, 0x66, 0x3e, 0x1d, 0x4c, 0x5f, 0xfe, 0xc0, 0x04, 0x43, 0xd6, 0x44, 0x19, 0xb5, 0xad, 0xc7, 0x22, 0xdc, 0x71, 0x28, 0x64, 0xde, 0x41, 0x38, 0x27, 0x8f, 0x2c, 0x6b, 0x08, 0xb8, 0xb8, 0x7b}}},
@@ -1366,7 +1227,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x42, 0xa3, 0xf1, 0xc5, 0xb4, 0x0f, 0xd8, 0xc8, 0x8d, 0x15, 0x31, 0xbd, 0xf8, 0x07, 0x8b, 0xcd, 0x08, 0x8a, 0xfb, 0x18, 0x07, 0xfe, 0x8e, 0x52, 0x86, 0xef, 0xbe, 0xec, 0x49, 0x52, 0x99, 0x08}}},
{{{0x0f, 0xa9, 0xd5, 0x01, 0xaa, 0x48, 0x4f, 0x28, 0x66, 0x32, 0x1a, 0xba, 0x7c, 0xea, 0x11, 0x80, 0x17, 0x18, 0x9b, 0x56, 0x88, 0x25, 0x06, 0x69, 0x12, 0x2c, 0xea, 0x56, 0x69, 0x41, 0x24, 0x19}} ,
{{0xde, 0x21, 0xf0, 0xda, 0x8a, 0xfb, 0xb1, 0xb8, 0xcd, 0xc8, 0x6a, 0x82, 0x19, 0x73, 0xdb, 0xc7, 0xcf, 0x88, 0xeb, 0x96, 0xee, 0x6f, 0xfb, 0x06, 0xd2, 0xcd, 0x7d, 0x7b, 0x12, 0x28, 0x8e, 0x0c}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x93, 0x44, 0x97, 0xce, 0x28, 0xff, 0x3a, 0x40, 0xc4, 0xf5, 0xf6, 0x9b, 0xf4, 0x6b, 0x07, 0x84, 0xfb, 0x98, 0xd8, 0xec, 0x8c, 0x03, 0x57, 0xec, 0x49, 0xed, 0x63, 0xb6, 0xaa, 0xff, 0x98, 0x28}} ,
{{0x3d, 0x16, 0x35, 0xf3, 0x46, 0xbc, 0xb3, 0xf4, 0xc6, 0xb6, 0x4f, 0xfa, 0xf4, 0xa0, 0x13, 0xe6, 0x57, 0x45, 0x93, 0xb9, 0xbc, 0xd6, 0x59, 0xe7, 0x77, 0x94, 0x6c, 0xab, 0x96, 0x3b, 0x4f, 0x09}}},
@@ -1376,7 +1237,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xcf, 0xe8, 0x0e, 0x6b, 0x96, 0x7d, 0xed, 0x27, 0xd1, 0x3c, 0xa9, 0xd9, 0x50, 0xa9, 0x98, 0x84, 0x5e, 0x86, 0xef, 0xd6, 0xf0, 0xf8, 0x0e, 0x89, 0x05, 0x2f, 0xd9, 0x5f, 0x15, 0x5f, 0x73, 0x79}}},
{{{0xc8, 0x5c, 0x16, 0xfe, 0xed, 0x9f, 0x26, 0x56, 0xf6, 0x4b, 0x9f, 0xa7, 0x0a, 0x85, 0xfe, 0xa5, 0x8c, 0x87, 0xdd, 0x98, 0xce, 0x4e, 0xc3, 0x58, 0x55, 0xb2, 0x7b, 0x3d, 0xd8, 0x6b, 0xb5, 0x4c}} ,
{{0x65, 0x38, 0xa0, 0x15, 0xfa, 0xa7, 0xb4, 0x8f, 0xeb, 0xc4, 0x86, 0x9b, 0x30, 0xa5, 0x5e, 0x4d, 0xea, 0x8a, 0x9a, 0x9f, 0x1a, 0xd8, 0x5b, 0x53, 0x14, 0x19, 0x25, 0x63, 0xb4, 0x6f, 0x1f, 0x5d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xac, 0x8f, 0xbc, 0x1e, 0x7d, 0x8b, 0x5a, 0x0b, 0x8d, 0xaf, 0x76, 0x2e, 0x71, 0xe3, 0x3b, 0x6f, 0x53, 0x2f, 0x3e, 0x90, 0x95, 0xd4, 0x35, 0x14, 0x4f, 0x8c, 0x3c, 0xce, 0x57, 0x1c, 0x76, 0x49}} ,
{{0xa8, 0x50, 0xe1, 0x61, 0x6b, 0x57, 0x35, 0xeb, 0x44, 0x0b, 0x0c, 0x6e, 0xf9, 0x25, 0x80, 0x74, 0xf2, 0x8f, 0x6f, 0x7a, 0x3e, 0x7f, 0x2d, 0xf3, 0x4e, 0x09, 0x65, 0x10, 0x5e, 0x03, 0x25, 0x32}}},
@@ -1386,7 +1247,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x97, 0x67, 0x9e, 0xeb, 0x6a, 0xf9, 0x6e, 0xd6, 0x73, 0xe8, 0x6b, 0x29, 0xec, 0x63, 0x82, 0x00, 0xa8, 0x99, 0x1c, 0x1d, 0x30, 0xc8, 0x90, 0x52, 0x90, 0xb6, 0x6a, 0x80, 0x4e, 0xff, 0x4b, 0x51}}},
{{{0x0f, 0x7d, 0x63, 0x8c, 0x6e, 0x5c, 0xde, 0x30, 0xdf, 0x65, 0xfa, 0x2e, 0xb0, 0xa3, 0x25, 0x05, 0x54, 0xbd, 0x25, 0xba, 0x06, 0xae, 0xdf, 0x8b, 0xd9, 0x1b, 0xea, 0x38, 0xb3, 0x05, 0x16, 0x09}} ,
{{0xc7, 0x8c, 0xbf, 0x64, 0x28, 0xad, 0xf8, 0xa5, 0x5a, 0x6f, 0xc9, 0xba, 0xd5, 0x7f, 0xd5, 0xd6, 0xbd, 0x66, 0x2f, 0x3d, 0xaa, 0x54, 0xf6, 0xba, 0x32, 0x22, 0x9a, 0x1e, 0x52, 0x05, 0xf4, 0x1d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xaa, 0x1f, 0xbb, 0xeb, 0xfe, 0xe4, 0x87, 0xfc, 0xb1, 0x2c, 0xb7, 0x88, 0xf4, 0xc6, 0xb9, 0xf5, 0x24, 0x46, 0xf2, 0xa5, 0x9f, 0x8f, 0x8a, 0x93, 0x70, 0x69, 0xd4, 0x56, 0xec, 0xfd, 0x06, 0x46}} ,
{{0x4e, 0x66, 0xcf, 0x4e, 0x34, 0xce, 0x0c, 0xd9, 0xa6, 0x50, 0xd6, 0x5e, 0x95, 0xaf, 0xe9, 0x58, 0xfa, 0xee, 0x9b, 0xb8, 0xa5, 0x0f, 0x35, 0xe0, 0x43, 0x82, 0x6d, 0x65, 0xe6, 0xd9, 0x00, 0x0f}}},
@@ -1396,7 +1257,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x99, 0xeb, 0x7c, 0x31, 0x73, 0x64, 0x67, 0x7f, 0x0c, 0x66, 0xaa, 0x8c, 0x69, 0x91, 0xe2, 0x26, 0xd3, 0x23, 0xe2, 0x76, 0x5d, 0x32, 0x52, 0xdf, 0x5d, 0xc5, 0x8f, 0xb7, 0x7c, 0x84, 0xb3, 0x70}}},
{{{0xeb, 0x01, 0xc7, 0x36, 0x97, 0x4e, 0xb6, 0xab, 0x5f, 0x0d, 0x2c, 0xba, 0x67, 0x64, 0x55, 0xde, 0xbc, 0xff, 0xa6, 0xec, 0x04, 0xd3, 0x8d, 0x39, 0x56, 0x5e, 0xee, 0xf8, 0xe4, 0x2e, 0x33, 0x62}} ,
{{0x65, 0xef, 0xb8, 0x9f, 0xc8, 0x4b, 0xa7, 0xfd, 0x21, 0x49, 0x9b, 0x92, 0x35, 0x82, 0xd6, 0x0a, 0x9b, 0xf2, 0x79, 0xf1, 0x47, 0x2f, 0x6a, 0x7e, 0x9f, 0xcf, 0x18, 0x02, 0x3c, 0xfb, 0x1b, 0x3e}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x2f, 0x8b, 0xc8, 0x40, 0x51, 0xd1, 0xac, 0x1a, 0x0b, 0xe4, 0xa9, 0xa2, 0x42, 0x21, 0x19, 0x2f, 0x7b, 0x97, 0xbf, 0xf7, 0x57, 0x6d, 0x3f, 0x3d, 0x4f, 0x0f, 0xe2, 0xb2, 0x81, 0x00, 0x9e, 0x7b}} ,
{{0x8c, 0x85, 0x2b, 0xc4, 0xfc, 0xf1, 0xab, 0xe8, 0x79, 0x22, 0xc4, 0x84, 0x17, 0x3a, 0xfa, 0x86, 0xa6, 0x7d, 0xf9, 0xf3, 0x6f, 0x03, 0x57, 0x20, 0x4d, 0x79, 0xf9, 0x6e, 0x71, 0x54, 0x38, 0x09}}},
@@ -1406,7 +1267,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x57, 0x82, 0x1c, 0xab, 0xbf, 0x62, 0x70, 0xe8, 0xc4, 0xcf, 0xf0, 0x28, 0x6e, 0x16, 0x3c, 0x08, 0x78, 0x89, 0x85, 0x46, 0x0f, 0xf6, 0x7f, 0xcf, 0xcb, 0x7e, 0xb8, 0x25, 0xe9, 0x5a, 0xfa, 0x03}}},
{{{0xfb, 0x95, 0x92, 0x63, 0x50, 0xfc, 0x62, 0xf0, 0xa4, 0x5e, 0x8c, 0x18, 0xc2, 0x17, 0x24, 0xb7, 0x78, 0xc2, 0xa9, 0xe7, 0x6a, 0x32, 0xd6, 0x29, 0x85, 0xaf, 0xcb, 0x8d, 0x91, 0x13, 0xda, 0x6b}} ,
{{0x36, 0x0a, 0xc2, 0xb6, 0x4b, 0xa5, 0x5d, 0x07, 0x17, 0x41, 0x31, 0x5f, 0x62, 0x46, 0xf8, 0x92, 0xf9, 0x66, 0x48, 0x73, 0xa6, 0x97, 0x0d, 0x7d, 0x88, 0xee, 0x62, 0xb1, 0x03, 0xa8, 0x3f, 0x2c}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x4a, 0xb1, 0x70, 0x8a, 0xa9, 0xe8, 0x63, 0x79, 0x00, 0xe2, 0x25, 0x16, 0xca, 0x4b, 0x0f, 0xa4, 0x66, 0xad, 0x19, 0x9f, 0x88, 0x67, 0x0c, 0x8b, 0xc2, 0x4a, 0x5b, 0x2b, 0x6d, 0x95, 0xaf, 0x19}} ,
{{0x8b, 0x9d, 0xb6, 0xcc, 0x60, 0xb4, 0x72, 0x4f, 0x17, 0x69, 0x5a, 0x4a, 0x68, 0x34, 0xab, 0xa1, 0x45, 0x32, 0x3c, 0x83, 0x87, 0x72, 0x30, 0x54, 0x77, 0x68, 0xae, 0xfb, 0xb5, 0x8b, 0x22, 0x5e}}},
@@ -1416,7 +1277,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x32, 0x65, 0x03, 0xe5, 0x89, 0xa6, 0x6e, 0xb3, 0x5b, 0x8e, 0xca, 0xeb, 0xfe, 0x22, 0x56, 0x8b, 0x5d, 0x14, 0x4b, 0x4d, 0xf9, 0xbe, 0xb5, 0xf5, 0xe6, 0x5c, 0x7b, 0x8b, 0xf4, 0x13, 0x11, 0x34}}},
{{{0x07, 0xc6, 0x22, 0x15, 0xe2, 0x9c, 0x60, 0xa2, 0x19, 0xd9, 0x27, 0xae, 0x37, 0x4e, 0xa6, 0xc9, 0x80, 0xa6, 0x91, 0x8f, 0x12, 0x49, 0xe5, 0x00, 0x18, 0x47, 0xd1, 0xd7, 0x28, 0x22, 0x63, 0x39}} ,
{{0xe8, 0xe2, 0x00, 0x7e, 0xf2, 0x9e, 0x1e, 0x99, 0x39, 0x95, 0x04, 0xbd, 0x1e, 0x67, 0x7b, 0xb2, 0x26, 0xac, 0xe6, 0xaa, 0xe2, 0x46, 0xd5, 0xe4, 0xe8, 0x86, 0xbd, 0xab, 0x7c, 0x55, 0x59, 0x6f}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x24, 0x64, 0x6e, 0x9b, 0x35, 0x71, 0x78, 0xce, 0x33, 0x03, 0x21, 0x33, 0x36, 0xf1, 0x73, 0x9b, 0xb9, 0x15, 0x8b, 0x2c, 0x69, 0xcf, 0x4d, 0xed, 0x4f, 0x4d, 0x57, 0x14, 0x13, 0x82, 0xa4, 0x4d}} ,
{{0x65, 0x6e, 0x0a, 0xa4, 0x59, 0x07, 0x17, 0xf2, 0x6b, 0x4a, 0x1f, 0x6e, 0xf6, 0xb5, 0xbc, 0x62, 0xe4, 0xb6, 0xda, 0xa2, 0x93, 0xbc, 0x29, 0x05, 0xd2, 0xd2, 0x73, 0x46, 0x03, 0x16, 0x40, 0x31}}},
@@ -1426,7 +1287,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x86, 0x17, 0x90, 0xe7, 0xc9, 0x1f, 0x10, 0xa5, 0x6a, 0x2d, 0x39, 0xd0, 0x3b, 0xc4, 0xa6, 0xe9, 0x59, 0x13, 0xda, 0x1a, 0xe6, 0xa0, 0xb9, 0x3c, 0x50, 0xb8, 0x40, 0x7c, 0x15, 0x36, 0x5a, 0x42}}},
{{{0xb4, 0x0b, 0x32, 0xab, 0xdc, 0x04, 0x51, 0x55, 0x21, 0x1e, 0x0b, 0x75, 0x99, 0x89, 0x73, 0x35, 0x3a, 0x91, 0x2b, 0xfe, 0xe7, 0x49, 0xea, 0x76, 0xc1, 0xf9, 0x46, 0xb9, 0x53, 0x02, 0x23, 0x04}} ,
{{0xfc, 0x5a, 0x1e, 0x1d, 0x74, 0x58, 0x95, 0xa6, 0x8f, 0x7b, 0x97, 0x3e, 0x17, 0x3b, 0x79, 0x2d, 0xa6, 0x57, 0xef, 0x45, 0x02, 0x0b, 0x4d, 0x6e, 0x9e, 0x93, 0x8d, 0x2f, 0xd9, 0x9d, 0xdb, 0x04}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xc0, 0xd7, 0x56, 0x97, 0x58, 0x91, 0xde, 0x09, 0x4f, 0x9f, 0xbe, 0x63, 0xb0, 0x83, 0x86, 0x43, 0x5d, 0xbc, 0xe0, 0xf3, 0xc0, 0x75, 0xbf, 0x8b, 0x8e, 0xaa, 0xf7, 0x8b, 0x64, 0x6e, 0xb0, 0x63}} ,
{{0x16, 0xae, 0x8b, 0xe0, 0x9b, 0x24, 0x68, 0x5c, 0x44, 0xc2, 0xd0, 0x08, 0xb7, 0x7b, 0x62, 0xfd, 0x7f, 0xd8, 0xd4, 0xb7, 0x50, 0xfd, 0x2c, 0x1b, 0xbf, 0x41, 0x95, 0xd9, 0x8e, 0xd8, 0x17, 0x1b}}},
@@ -1436,7 +1297,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x02, 0xfb, 0x54, 0xb8, 0x05, 0xf3, 0x81, 0x52, 0x69, 0x34, 0x46, 0x9d, 0x86, 0x76, 0x8f, 0xd7, 0xf8, 0x6a, 0x66, 0xff, 0xe6, 0xa7, 0x90, 0xf7, 0x5e, 0xcd, 0x6a, 0x9b, 0x55, 0xfc, 0x9d, 0x48}}},
{{{0xbd, 0xaa, 0x13, 0xe6, 0xcd, 0x45, 0x4a, 0xa4, 0x59, 0x0a, 0x64, 0xb1, 0x98, 0xd6, 0x34, 0x13, 0x04, 0xe6, 0x97, 0x94, 0x06, 0xcb, 0xd4, 0x4e, 0xbb, 0x96, 0xcd, 0xd1, 0x57, 0xd1, 0xe3, 0x06}} ,
{{0x7a, 0x6c, 0x45, 0x27, 0xc4, 0x93, 0x7f, 0x7d, 0x7c, 0x62, 0x50, 0x38, 0x3a, 0x6b, 0xb5, 0x88, 0xc6, 0xd9, 0xf1, 0x78, 0x19, 0xb9, 0x39, 0x93, 0x3d, 0xc9, 0xe0, 0x9c, 0x3c, 0xce, 0xf5, 0x72}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x24, 0xea, 0x23, 0x7d, 0x56, 0x2c, 0xe2, 0x59, 0x0e, 0x85, 0x60, 0x04, 0x88, 0x5a, 0x74, 0x1e, 0x4b, 0xef, 0x13, 0xda, 0x4c, 0xff, 0x83, 0x45, 0x85, 0x3f, 0x08, 0x95, 0x2c, 0x20, 0x13, 0x1f}} ,
{{0x48, 0x5f, 0x27, 0x90, 0x5c, 0x02, 0x42, 0xad, 0x78, 0x47, 0x5c, 0xb5, 0x7e, 0x08, 0x85, 0x00, 0xfa, 0x7f, 0xfd, 0xfd, 0xe7, 0x09, 0x11, 0xf2, 0x7e, 0x1b, 0x38, 0x6c, 0x35, 0x6d, 0x33, 0x66}}},
@@ -1446,7 +1307,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x4e, 0x3b, 0x69, 0xc8, 0x43, 0x75, 0x51, 0x6c, 0x79, 0x56, 0xe4, 0xcb, 0xf7, 0xa6, 0x51, 0xc2, 0x2c, 0x42, 0x0b, 0xd4, 0x82, 0x20, 0x1c, 0x01, 0x08, 0x66, 0xd7, 0xbf, 0x04, 0x56, 0xfc, 0x02}}},
{{{0x24, 0xe8, 0xb7, 0x60, 0xae, 0x47, 0x80, 0xfc, 0xe5, 0x23, 0xe7, 0xc2, 0xc9, 0x85, 0xe6, 0x98, 0xa0, 0x29, 0x4e, 0xe1, 0x84, 0x39, 0x2d, 0x95, 0x2c, 0xf3, 0x45, 0x3c, 0xff, 0xaf, 0x27, 0x4c}} ,
{{0x6b, 0xa6, 0xf5, 0x4b, 0x11, 0xbd, 0xba, 0x5b, 0x9e, 0xc4, 0xa4, 0x51, 0x1e, 0xbe, 0xd0, 0x90, 0x3a, 0x9c, 0xc2, 0x26, 0xb6, 0x1e, 0xf1, 0x95, 0x7d, 0xc8, 0x6d, 0x52, 0xe6, 0x99, 0x2c, 0x5f}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x85, 0xe0, 0x24, 0x32, 0xb4, 0xd1, 0xef, 0xfc, 0x69, 0xa2, 0xbf, 0x8f, 0x72, 0x2c, 0x95, 0xf6, 0xe4, 0x6e, 0x7d, 0x90, 0xf7, 0x57, 0x81, 0xa0, 0xf7, 0xda, 0xef, 0x33, 0x07, 0xe3, 0x6b, 0x78}} ,
{{0x36, 0x27, 0x3e, 0xc6, 0x12, 0x07, 0xab, 0x4e, 0xbe, 0x69, 0x9d, 0xb3, 0xbe, 0x08, 0x7c, 0x2a, 0x47, 0x08, 0xfd, 0xd4, 0xcd, 0x0e, 0x27, 0x34, 0x5b, 0x98, 0x34, 0x2f, 0x77, 0x5f, 0x3a, 0x65}}},
@@ -1456,7 +1317,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x77, 0x5b, 0xe2, 0x37, 0xc7, 0xe1, 0x7c, 0x13, 0x8c, 0x9f, 0x7b, 0x7b, 0x2a, 0xce, 0x42, 0xa3, 0xb9, 0x2a, 0x99, 0xa8, 0xc0, 0xd8, 0x3c, 0x86, 0xb0, 0xfb, 0xe9, 0x76, 0x77, 0xf7, 0xf5, 0x56}}},
{{{0xdf, 0xb3, 0x46, 0x11, 0x6e, 0x13, 0xb7, 0x28, 0x4e, 0x56, 0xdd, 0xf1, 0xac, 0xad, 0x58, 0xc3, 0xf8, 0x88, 0x94, 0x5e, 0x06, 0x98, 0xa1, 0xe4, 0x6a, 0xfb, 0x0a, 0x49, 0x5d, 0x8a, 0xfe, 0x77}} ,
{{0x46, 0x02, 0xf5, 0xa5, 0xaf, 0xc5, 0x75, 0x6d, 0xba, 0x45, 0x35, 0x0a, 0xfe, 0xc9, 0xac, 0x22, 0x91, 0x8d, 0x21, 0x95, 0x33, 0x03, 0xc0, 0x8a, 0x16, 0xf3, 0x39, 0xe0, 0x01, 0x0f, 0x53, 0x3c}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x34, 0x75, 0x37, 0x1f, 0x34, 0x4e, 0xa9, 0x1d, 0x68, 0x67, 0xf8, 0x49, 0x98, 0x96, 0xfc, 0x4c, 0x65, 0x97, 0xf7, 0x02, 0x4a, 0x52, 0x6c, 0x01, 0xbd, 0x48, 0xbb, 0x1b, 0xed, 0xa4, 0xe2, 0x53}} ,
{{0x59, 0xd5, 0x9b, 0x5a, 0xa2, 0x90, 0xd3, 0xb8, 0x37, 0x4c, 0x55, 0x82, 0x28, 0x08, 0x0f, 0x7f, 0xaa, 0x81, 0x65, 0xe0, 0x0c, 0x52, 0xc9, 0xa3, 0x32, 0x27, 0x64, 0xda, 0xfd, 0x34, 0x23, 0x5a}}},
@@ -1466,7 +1327,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x1d, 0x29, 0x7a, 0xa1, 0xec, 0x8e, 0xb5, 0xad, 0xea, 0x02, 0x68, 0x60, 0x74, 0x29, 0x1c, 0xa5, 0xcf, 0xc8, 0x3b, 0x7d, 0x8b, 0x2b, 0x7c, 0xad, 0xa4, 0x40, 0x17, 0x51, 0x59, 0x7c, 0x2e, 0x5d}}},
{{{0x0a, 0x6c, 0x4f, 0xbc, 0x3e, 0x32, 0xe7, 0x4a, 0x1a, 0x13, 0xc1, 0x49, 0x38, 0xbf, 0xf7, 0xc2, 0xd3, 0x8f, 0x6b, 0xad, 0x52, 0xf7, 0xcf, 0xbc, 0x27, 0xcb, 0x40, 0x67, 0x76, 0xcd, 0x6d, 0x56}} ,
{{0xe5, 0xb0, 0x27, 0xad, 0xbe, 0x9b, 0xf2, 0xb5, 0x63, 0xde, 0x3a, 0x23, 0x95, 0xb7, 0x0a, 0x7e, 0xf3, 0x9e, 0x45, 0x6f, 0x19, 0x39, 0x75, 0x8f, 0x39, 0x3d, 0x0f, 0xc0, 0x9f, 0xf1, 0xe9, 0x51}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x88, 0xaa, 0x14, 0x24, 0x86, 0x94, 0x11, 0x12, 0x3e, 0x1a, 0xb5, 0xcc, 0xbb, 0xe0, 0x9c, 0xd5, 0x9c, 0x6d, 0xba, 0x58, 0x72, 0x8d, 0xfb, 0x22, 0x7b, 0x9f, 0x7c, 0x94, 0x30, 0xb3, 0x51, 0x21}} ,
{{0xf6, 0x74, 0x3d, 0xf2, 0xaf, 0xd0, 0x1e, 0x03, 0x7c, 0x23, 0x6b, 0xc9, 0xfc, 0x25, 0x70, 0x90, 0xdc, 0x9a, 0xa4, 0xfb, 0x49, 0xfc, 0x3d, 0x0a, 0x35, 0x38, 0x6f, 0xe4, 0x7e, 0x50, 0x01, 0x2a}}},
@@ -1476,7 +1337,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x84, 0x7c, 0xc2, 0xa6, 0x91, 0x23, 0xce, 0xbd, 0xdc, 0xf9, 0xce, 0xd5, 0x75, 0x30, 0x22, 0xe6, 0xf9, 0x43, 0x62, 0x0d, 0xf7, 0x75, 0x9d, 0x7f, 0x8c, 0xff, 0x7d, 0xe4, 0x72, 0xac, 0x9f, 0x1c}}},
{{{0x88, 0xc1, 0x99, 0xd0, 0x3c, 0x1c, 0x5d, 0xb4, 0xef, 0x13, 0x0f, 0x90, 0xb9, 0x36, 0x2f, 0x95, 0x95, 0xc6, 0xdc, 0xde, 0x0a, 0x51, 0xe2, 0x8d, 0xf3, 0xbc, 0x51, 0xec, 0xdf, 0xb1, 0xa2, 0x5f}} ,
{{0x2e, 0x68, 0xa1, 0x23, 0x7d, 0x9b, 0x40, 0x69, 0x85, 0x7b, 0x42, 0xbf, 0x90, 0x4b, 0xd6, 0x40, 0x2f, 0xd7, 0x52, 0x52, 0xb2, 0x21, 0xde, 0x64, 0xbd, 0x88, 0xc3, 0x6d, 0xa5, 0xfa, 0x81, 0x3f}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xfb, 0xfd, 0x47, 0x7b, 0x8a, 0x66, 0x9e, 0x79, 0x2e, 0x64, 0x82, 0xef, 0xf7, 0x21, 0xec, 0xf6, 0xd8, 0x86, 0x09, 0x31, 0x7c, 0xdd, 0x03, 0x6a, 0x58, 0xa0, 0x77, 0xb7, 0x9b, 0x8c, 0x87, 0x1f}} ,
{{0x55, 0x47, 0xe4, 0xa8, 0x3d, 0x55, 0x21, 0x34, 0xab, 0x1d, 0xae, 0xe0, 0xf4, 0xea, 0xdb, 0xc5, 0xb9, 0x58, 0xbf, 0xc4, 0x2a, 0x89, 0x31, 0x1a, 0xf4, 0x2d, 0xe1, 0xca, 0x37, 0x99, 0x47, 0x59}}},
@@ -1486,7 +1347,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x29, 0xac, 0xff, 0x27, 0xe0, 0x59, 0xbe, 0x39, 0x9c, 0x0d, 0x83, 0xd7, 0x10, 0x0b, 0x15, 0xb7, 0xe1, 0xc2, 0x2c, 0x30, 0x73, 0x80, 0x3a, 0x7d, 0x5d, 0xab, 0x58, 0x6b, 0xc1, 0xf0, 0xf4, 0x22}}},
{{{0xfe, 0x7f, 0xfb, 0x35, 0x7d, 0xc6, 0x01, 0x23, 0x28, 0xc4, 0x02, 0xac, 0x1f, 0x42, 0xb4, 0x9d, 0xfc, 0x00, 0x94, 0xa5, 0xee, 0xca, 0xda, 0x97, 0x09, 0x41, 0x77, 0x87, 0x5d, 0x7b, 0x87, 0x78}} ,
{{0xf5, 0xfb, 0x90, 0x2d, 0x81, 0x19, 0x9e, 0x2f, 0x6d, 0x85, 0x88, 0x8c, 0x40, 0x5c, 0x77, 0x41, 0x4d, 0x01, 0x19, 0x76, 0x60, 0xe8, 0x4c, 0x48, 0xe4, 0x33, 0x83, 0x32, 0x6c, 0xb4, 0x41, 0x03}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xff, 0x10, 0xc2, 0x09, 0x4f, 0x6e, 0xf4, 0xd2, 0xdf, 0x7e, 0xca, 0x7b, 0x1c, 0x1d, 0xba, 0xa3, 0xb6, 0xda, 0x67, 0x33, 0xd4, 0x87, 0x36, 0x4b, 0x11, 0x20, 0x05, 0xa6, 0x29, 0xc1, 0x87, 0x17}} ,
{{0xf6, 0x96, 0xca, 0x2f, 0xda, 0x38, 0xa7, 0x1b, 0xfc, 0xca, 0x7d, 0xfe, 0x08, 0x89, 0xe2, 0x47, 0x2b, 0x6a, 0x5d, 0x4b, 0xfa, 0xa1, 0xb4, 0xde, 0xb6, 0xc2, 0x31, 0x51, 0xf5, 0xe0, 0xa4, 0x0b}}},
@@ -1496,7 +1357,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x0c, 0x0e, 0xf3, 0xba, 0xf0, 0xe5, 0xba, 0xb2, 0x57, 0x77, 0xc6, 0x20, 0x9b, 0x89, 0x24, 0xbe, 0xf2, 0x9c, 0x8a, 0xba, 0x69, 0xc1, 0xf1, 0xb0, 0x4f, 0x2a, 0x05, 0x9a, 0xee, 0x10, 0x7e, 0x36}}},
{{{0x3f, 0x26, 0xe9, 0x40, 0xe9, 0x03, 0xad, 0x06, 0x69, 0x91, 0xe0, 0xd1, 0x89, 0x60, 0x84, 0x79, 0xde, 0x27, 0x6d, 0xe6, 0x76, 0xbd, 0xea, 0xe6, 0xae, 0x48, 0xc3, 0x67, 0xc0, 0x57, 0xcd, 0x2f}} ,
{{0x7f, 0xc1, 0xdc, 0xb9, 0xc7, 0xbc, 0x86, 0x3d, 0x55, 0x4b, 0x28, 0x7a, 0xfb, 0x4d, 0xc7, 0xf8, 0xbc, 0x67, 0x2a, 0x60, 0x4d, 0x8f, 0x07, 0x0b, 0x1a, 0x17, 0xbf, 0xfa, 0xac, 0xa7, 0x3d, 0x1a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x91, 0x3f, 0xed, 0x5e, 0x18, 0x78, 0x3f, 0x23, 0x2c, 0x0d, 0x8c, 0x44, 0x00, 0xe8, 0xfb, 0xe9, 0x8e, 0xd6, 0xd1, 0x36, 0x58, 0x57, 0x9e, 0xae, 0x4b, 0x5c, 0x0b, 0x07, 0xbc, 0x6b, 0x55, 0x2b}} ,
{{0x6f, 0x4d, 0x17, 0xd7, 0xe1, 0x84, 0xd9, 0x78, 0xb1, 0x90, 0xfd, 0x2e, 0xb3, 0xb5, 0x19, 0x3f, 0x1b, 0xfa, 0xc0, 0x68, 0xb3, 0xdd, 0x00, 0x2e, 0x89, 0xbd, 0x7e, 0x80, 0x32, 0x13, 0xa0, 0x7b}}},
@@ -1506,7 +1367,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x01, 0xb8, 0xd6, 0x16, 0x67, 0xa0, 0x15, 0xb9, 0xe1, 0x58, 0xa4, 0xa7, 0x31, 0x37, 0x77, 0x2f, 0x8b, 0x12, 0x9f, 0xf4, 0x3f, 0xc7, 0x36, 0x66, 0xd2, 0xa8, 0x56, 0xf7, 0x7f, 0x74, 0xc6, 0x41}}},
{{{0x5d, 0xf8, 0xb4, 0xa8, 0x30, 0xdd, 0xcc, 0x38, 0xa5, 0xd3, 0xca, 0xd8, 0xd1, 0xf8, 0xb2, 0x31, 0x91, 0xd4, 0x72, 0x05, 0x57, 0x4a, 0x3b, 0x82, 0x4a, 0xc6, 0x68, 0x20, 0xe2, 0x18, 0x41, 0x61}} ,
{{0x19, 0xd4, 0x8d, 0x47, 0x29, 0x12, 0x65, 0xb0, 0x11, 0x78, 0x47, 0xb5, 0xcb, 0xa3, 0xa5, 0xfa, 0x05, 0x85, 0x54, 0xa9, 0x33, 0x97, 0x8d, 0x2b, 0xc2, 0xfe, 0x99, 0x35, 0x28, 0xe5, 0xeb, 0x63}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xb1, 0x3f, 0x3f, 0xef, 0xd8, 0xf4, 0xfc, 0xb3, 0xa0, 0x60, 0x50, 0x06, 0x2b, 0x29, 0x52, 0x70, 0x15, 0x0b, 0x24, 0x24, 0xf8, 0x5f, 0x79, 0x18, 0xcc, 0xff, 0x89, 0x99, 0x84, 0xa1, 0xae, 0x13}} ,
{{0x44, 0x1f, 0xb8, 0xc2, 0x01, 0xc1, 0x30, 0x19, 0x55, 0x05, 0x60, 0x10, 0xa4, 0x6c, 0x2d, 0x67, 0x70, 0xe5, 0x25, 0x1b, 0xf2, 0xbf, 0xdd, 0xfb, 0x70, 0x2b, 0xa1, 0x8c, 0x9c, 0x94, 0x84, 0x08}}},
@@ -1516,7 +1377,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xdc, 0xff, 0x15, 0x61, 0x2f, 0x4a, 0x2f, 0x62, 0xf2, 0x04, 0x2f, 0xb5, 0x0c, 0xb7, 0x1e, 0x3f, 0x74, 0x1a, 0x0f, 0xd7, 0xea, 0xcd, 0xd9, 0x7d, 0xf6, 0x12, 0x0e, 0x2f, 0xdb, 0x5a, 0x3b, 0x16}}},
{{{0x1b, 0x37, 0x47, 0xe3, 0xf5, 0x9e, 0xea, 0x2c, 0x2a, 0xe7, 0x82, 0x36, 0xf4, 0x1f, 0x81, 0x47, 0x92, 0x4b, 0x69, 0x0e, 0x11, 0x8c, 0x5d, 0x53, 0x5b, 0x81, 0x27, 0x08, 0xbc, 0xa0, 0xae, 0x25}} ,
{{0x69, 0x32, 0xa1, 0x05, 0x11, 0x42, 0x00, 0xd2, 0x59, 0xac, 0x4d, 0x62, 0x8b, 0x13, 0xe2, 0x50, 0x5d, 0xa0, 0x9d, 0x9b, 0xfd, 0xbb, 0x12, 0x41, 0x75, 0x41, 0x9e, 0xcc, 0xdc, 0xc7, 0xdc, 0x5d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xd9, 0xe3, 0x38, 0x06, 0x46, 0x70, 0x82, 0x5e, 0x28, 0x49, 0x79, 0xff, 0x25, 0xd2, 0x4e, 0x29, 0x8d, 0x06, 0xb0, 0x23, 0xae, 0x9b, 0x66, 0xe4, 0x7d, 0xc0, 0x70, 0x91, 0xa3, 0xfc, 0xec, 0x4e}} ,
{{0x62, 0x12, 0x37, 0x6a, 0x30, 0xf6, 0x1e, 0xfb, 0x14, 0x5c, 0x0d, 0x0e, 0xb7, 0x81, 0x6a, 0xe7, 0x08, 0x05, 0xac, 0xaa, 0x38, 0x46, 0xe2, 0x73, 0xea, 0x4b, 0x07, 0x81, 0x43, 0x7c, 0x9e, 0x5e}}},
@@ -1526,7 +1387,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xe5, 0x44, 0xec, 0x06, 0x9d, 0x90, 0x79, 0x9f, 0xd3, 0xe0, 0x79, 0xaf, 0x8f, 0x10, 0xfd, 0xdd, 0x04, 0xae, 0x27, 0x97, 0x46, 0x33, 0x79, 0xea, 0xb8, 0x4e, 0xca, 0x5a, 0x59, 0x57, 0xe1, 0x0e}}},
{{{0x1a, 0xda, 0xf3, 0xa5, 0x41, 0x43, 0x28, 0xfc, 0x7e, 0xe7, 0x71, 0xea, 0xc6, 0x3b, 0x59, 0xcc, 0x2e, 0xd3, 0x40, 0xec, 0xb3, 0x13, 0x6f, 0x44, 0xcd, 0x13, 0xb2, 0x37, 0xf2, 0x6e, 0xd9, 0x1c}} ,
{{0xe3, 0xdb, 0x60, 0xcd, 0x5c, 0x4a, 0x18, 0x0f, 0xef, 0x73, 0x36, 0x71, 0x8c, 0xf6, 0x11, 0xb4, 0xd8, 0xce, 0x17, 0x5e, 0x4f, 0x26, 0x77, 0x97, 0x5f, 0xcb, 0xef, 0x91, 0xeb, 0x6a, 0x62, 0x7a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x18, 0x4a, 0xa2, 0x97, 0x08, 0x81, 0x2d, 0x83, 0xc4, 0xcc, 0xf0, 0x83, 0x7e, 0xec, 0x0d, 0x95, 0x4c, 0x5b, 0xfb, 0xfa, 0x98, 0x80, 0x4a, 0x66, 0x56, 0x0c, 0x51, 0xb3, 0xf2, 0x04, 0x5d, 0x27}} ,
{{0x3b, 0xb9, 0xb8, 0x06, 0x5a, 0x2e, 0xfe, 0xc3, 0x82, 0x37, 0x9c, 0xa3, 0x11, 0x1f, 0x9c, 0xa6, 0xda, 0x63, 0x48, 0x9b, 0xad, 0xde, 0x2d, 0xa6, 0xbc, 0x6e, 0x32, 0xda, 0x27, 0x65, 0xdd, 0x57}}},
@@ -1536,7 +1397,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xa4, 0x4b, 0x62, 0x4c, 0xe6, 0xfd, 0x72, 0x07, 0xf2, 0x81, 0xfc, 0xf2, 0xbd, 0x12, 0x7c, 0x68, 0x76, 0x2a, 0xba, 0xf5, 0x65, 0xb1, 0x1f, 0x17, 0x0a, 0x38, 0xb0, 0xbf, 0xc0, 0xf8, 0xf4, 0x2a}}},
{{{0x55, 0x60, 0x55, 0x5b, 0xe4, 0x1d, 0x71, 0x4c, 0x9d, 0x5b, 0x9f, 0x70, 0xa6, 0x85, 0x9a, 0x2c, 0xa0, 0xe2, 0x32, 0x48, 0xce, 0x9e, 0x2a, 0xa5, 0x07, 0x3b, 0xc7, 0x6c, 0x86, 0x77, 0xde, 0x3c}} ,
{{0xf7, 0x18, 0x7a, 0x96, 0x7e, 0x43, 0x57, 0xa9, 0x55, 0xfc, 0x4e, 0xb6, 0x72, 0x00, 0xf2, 0xe4, 0xd7, 0x52, 0xd3, 0xd3, 0xb6, 0x85, 0xf6, 0x71, 0xc7, 0x44, 0x3f, 0x7f, 0xd7, 0xb3, 0xf2, 0x79}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x46, 0xca, 0xa7, 0x55, 0x7b, 0x79, 0xf3, 0xca, 0x5a, 0x65, 0xf6, 0xed, 0x50, 0x14, 0x7b, 0xe4, 0xc4, 0x2a, 0x65, 0x9e, 0xe2, 0xf9, 0xca, 0xa7, 0x22, 0x26, 0x53, 0xcb, 0x21, 0x5b, 0xa7, 0x31}} ,
{{0x90, 0xd7, 0xc5, 0x26, 0x08, 0xbd, 0xb0, 0x53, 0x63, 0x58, 0xc3, 0x31, 0x5e, 0x75, 0x46, 0x15, 0x91, 0xa6, 0xf8, 0x2f, 0x1a, 0x08, 0x65, 0x88, 0x2f, 0x98, 0x04, 0xf1, 0x7c, 0x6e, 0x00, 0x77}}},
@@ -1546,7 +1407,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x68, 0x0e, 0x6f, 0x97, 0xba, 0x70, 0xbb, 0xa3, 0x0e, 0xe5, 0x0b, 0x12, 0xf4, 0xa2, 0xdc, 0x47, 0xf8, 0xe6, 0xd0, 0x23, 0x6c, 0x33, 0xa8, 0x99, 0x46, 0x6e, 0x0f, 0x44, 0xba, 0x76, 0x48, 0x0f}}},
{{{0xa3, 0x2a, 0x61, 0x37, 0xe2, 0x59, 0x12, 0x0e, 0x27, 0xba, 0x64, 0x43, 0xae, 0xc0, 0x42, 0x69, 0x79, 0xa4, 0x1e, 0x29, 0x8b, 0x15, 0xeb, 0xf8, 0xaf, 0xd4, 0xa2, 0x68, 0x33, 0xb5, 0x7a, 0x24}} ,
{{0x2c, 0x19, 0x33, 0xdd, 0x1b, 0xab, 0xec, 0x01, 0xb0, 0x23, 0xf8, 0x42, 0x2b, 0x06, 0x88, 0xea, 0x3d, 0x2d, 0x00, 0x2a, 0x78, 0x45, 0x4d, 0x38, 0xed, 0x2e, 0x2e, 0x44, 0x49, 0xed, 0xcb, 0x33}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xa0, 0x68, 0xe8, 0x41, 0x8f, 0x91, 0xf8, 0x11, 0x13, 0x90, 0x2e, 0xa7, 0xab, 0x30, 0xef, 0xad, 0xa0, 0x61, 0x00, 0x88, 0xef, 0xdb, 0xce, 0x5b, 0x5c, 0xbb, 0x62, 0xc8, 0x56, 0xf9, 0x00, 0x73}} ,
{{0x3f, 0x60, 0xc1, 0x82, 0x2d, 0xa3, 0x28, 0x58, 0x24, 0x9e, 0x9f, 0xe3, 0x70, 0xcc, 0x09, 0x4e, 0x1a, 0x3f, 0x11, 0x11, 0x15, 0x07, 0x3c, 0xa4, 0x41, 0xe0, 0x65, 0xa3, 0x0a, 0x41, 0x6d, 0x11}}},
@@ -1556,7 +1417,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x32, 0xb4, 0x18, 0x47, 0x79, 0xcb, 0xd4, 0x5a, 0x07, 0x14, 0x0f, 0xa0, 0xd5, 0xac, 0xd0, 0x41, 0x40, 0xab, 0x61, 0x23, 0xe5, 0x2a, 0x2a, 0x6f, 0xf7, 0xa8, 0xd4, 0x76, 0xef, 0xe7, 0x45, 0x6c}}},
{{{0xa1, 0x5e, 0x60, 0x4f, 0xfb, 0xe1, 0x70, 0x6a, 0x1f, 0x55, 0x4f, 0x09, 0xb4, 0x95, 0x33, 0x36, 0xc6, 0x81, 0x01, 0x18, 0x06, 0x25, 0x27, 0xa4, 0xb4, 0x24, 0xa4, 0x86, 0x03, 0x4c, 0xac, 0x02}} ,
{{0x77, 0x38, 0xde, 0xd7, 0x60, 0x48, 0x07, 0xf0, 0x74, 0xa8, 0xff, 0x54, 0xe5, 0x30, 0x43, 0xff, 0x77, 0xfb, 0x21, 0x07, 0xff, 0xb2, 0x07, 0x6b, 0xe4, 0xe5, 0x30, 0xfc, 0x19, 0x6c, 0xa3, 0x01}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x13, 0xc5, 0x2c, 0xac, 0xd3, 0x83, 0x82, 0x7c, 0x29, 0xf7, 0x05, 0xa5, 0x00, 0xb6, 0x1f, 0x86, 0x55, 0xf4, 0xd6, 0x2f, 0x0c, 0x99, 0xd0, 0x65, 0x9b, 0x6b, 0x46, 0x0d, 0x43, 0xf8, 0x16, 0x28}} ,
{{0x1e, 0x7f, 0xb4, 0x74, 0x7e, 0xb1, 0x89, 0x4f, 0x18, 0x5a, 0xab, 0x64, 0x06, 0xdf, 0x45, 0x87, 0xe0, 0x6a, 0xc6, 0xf0, 0x0e, 0xc9, 0x24, 0x35, 0x38, 0xea, 0x30, 0x54, 0xb4, 0xc4, 0x52, 0x54}}},
@@ -1566,7 +1427,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x32, 0xad, 0xae, 0x85, 0x58, 0x43, 0xb8, 0xb1, 0xe6, 0x3e, 0x00, 0x9c, 0x78, 0x88, 0x56, 0xdb, 0x9c, 0xfc, 0x79, 0xf6, 0xf9, 0x41, 0x5f, 0xb7, 0xbc, 0x11, 0xf9, 0x20, 0x36, 0x1c, 0x53, 0x2b}}},
{{{0x5a, 0x20, 0x5b, 0xa1, 0xa5, 0x44, 0x91, 0x24, 0x02, 0x63, 0x12, 0x64, 0xb8, 0x55, 0xf6, 0xde, 0x2c, 0xdb, 0x47, 0xb8, 0xc6, 0x0a, 0xc3, 0x00, 0x78, 0x93, 0xd8, 0xf5, 0xf5, 0x18, 0x28, 0x0a}} ,
{{0xd6, 0x1b, 0x9a, 0x6c, 0xe5, 0x46, 0xea, 0x70, 0x96, 0x8d, 0x4e, 0x2a, 0x52, 0x21, 0x26, 0x4b, 0xb1, 0xbb, 0x0f, 0x7c, 0xa9, 0x9b, 0x04, 0xbb, 0x51, 0x08, 0xf1, 0x9a, 0xa4, 0x76, 0x7c, 0x18}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xfa, 0x94, 0xf7, 0x40, 0xd0, 0xd7, 0xeb, 0xa9, 0x82, 0x36, 0xd5, 0x15, 0xb9, 0x33, 0x7a, 0xbf, 0x8a, 0xf2, 0x63, 0xaa, 0x37, 0xf5, 0x59, 0xac, 0xbd, 0xbb, 0x32, 0x36, 0xbe, 0x73, 0x99, 0x38}} ,
{{0x2c, 0xb3, 0xda, 0x7a, 0xd8, 0x3d, 0x99, 0xca, 0xd2, 0xf4, 0xda, 0x99, 0x8e, 0x4f, 0x98, 0xb7, 0xf4, 0xae, 0x3e, 0x9f, 0x8e, 0x35, 0x60, 0xa4, 0x33, 0x75, 0xa4, 0x04, 0x93, 0xb1, 0x6b, 0x4d}}},
@@ -1576,7 +1437,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x53, 0xcf, 0xc2, 0xa1, 0xad, 0x6a, 0xf3, 0xcd, 0x8f, 0xc9, 0xde, 0x1c, 0xf8, 0x6c, 0x8f, 0xf8, 0x76, 0x42, 0xe7, 0xfe, 0xb2, 0x72, 0x21, 0x0a, 0x66, 0x74, 0x8f, 0xb7, 0xeb, 0xe4, 0x6f, 0x01}}},
{{{0x22, 0x8c, 0x6b, 0xbe, 0xfc, 0x4d, 0x70, 0x62, 0x6e, 0x52, 0x77, 0x99, 0x88, 0x7e, 0x7b, 0x57, 0x7a, 0x0d, 0xfe, 0xdc, 0x72, 0x92, 0xf1, 0x68, 0x1d, 0x97, 0xd7, 0x7c, 0x8d, 0x53, 0x10, 0x37}} ,
{{0x53, 0x88, 0x77, 0x02, 0xca, 0x27, 0xa8, 0xe5, 0x45, 0xe2, 0xa8, 0x48, 0x2a, 0xab, 0x18, 0xca, 0xea, 0x2d, 0x2a, 0x54, 0x17, 0x37, 0x32, 0x09, 0xdc, 0xe0, 0x4a, 0xb7, 0x7d, 0x82, 0x10, 0x7d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x8a, 0x64, 0x1e, 0x14, 0x0a, 0x57, 0xd4, 0xda, 0x5c, 0x96, 0x9b, 0x01, 0x4c, 0x67, 0xbf, 0x8b, 0x30, 0xfe, 0x08, 0xdb, 0x0d, 0xd5, 0xa8, 0xd7, 0x09, 0x11, 0x85, 0xa2, 0xd3, 0x45, 0xfb, 0x7e}} ,
{{0xda, 0x8c, 0xc2, 0xd0, 0xac, 0x18, 0xe8, 0x52, 0x36, 0xd4, 0x21, 0xa3, 0xdd, 0x57, 0x22, 0x79, 0xb7, 0xf8, 0x71, 0x9d, 0xc6, 0x91, 0x70, 0x86, 0x56, 0xbf, 0xa1, 0x11, 0x8b, 0x19, 0xe1, 0x0f}}},
@@ -1586,7 +1447,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x62, 0x4f, 0xd0, 0x75, 0x77, 0xba, 0x76, 0x77, 0xd7, 0xb8, 0xd8, 0x92, 0x6f, 0x98, 0x34, 0x3d, 0xd6, 0x4e, 0x1c, 0x0f, 0xf0, 0x8f, 0x2e, 0xf1, 0xb3, 0xbd, 0xb1, 0xb9, 0xec, 0x99, 0xb4, 0x07}}},
{{{0x60, 0x57, 0x2e, 0x9a, 0x72, 0x1d, 0x6b, 0x6e, 0x58, 0x33, 0x24, 0x8c, 0x48, 0x39, 0x46, 0x8e, 0x89, 0x6a, 0x88, 0x51, 0x23, 0x62, 0xb5, 0x32, 0x09, 0x36, 0xe3, 0x57, 0xf5, 0x98, 0xde, 0x6f}} ,
{{0x8b, 0x2c, 0x00, 0x48, 0x4a, 0xf9, 0x5b, 0x87, 0x69, 0x52, 0xe5, 0x5b, 0xd1, 0xb1, 0xe5, 0x25, 0x25, 0xe0, 0x9c, 0xc2, 0x13, 0x44, 0xe8, 0xb9, 0x0a, 0x70, 0xad, 0xbd, 0x0f, 0x51, 0x94, 0x69}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xa2, 0xdc, 0xab, 0xa9, 0x25, 0x2d, 0xac, 0x5f, 0x03, 0x33, 0x08, 0xe7, 0x7e, 0xfe, 0x95, 0x36, 0x3c, 0x5b, 0x3a, 0xd3, 0x05, 0x82, 0x1c, 0x95, 0x2d, 0xd8, 0x77, 0x7e, 0x02, 0xd9, 0x5b, 0x70}} ,
{{0xc2, 0xfe, 0x1b, 0x0c, 0x67, 0xcd, 0xd6, 0xe0, 0x51, 0x8e, 0x2c, 0xe0, 0x79, 0x88, 0xf0, 0xcf, 0x41, 0x4a, 0xad, 0x23, 0xd4, 0x46, 0xca, 0x94, 0xa1, 0xc3, 0xeb, 0x28, 0x06, 0xfa, 0x17, 0x14}}},
@@ -1596,7 +1457,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x64, 0xe9, 0x28, 0xc7, 0xa4, 0xcf, 0x2a, 0xf9, 0x90, 0x64, 0x72, 0x2c, 0x8b, 0xeb, 0xec, 0xa0, 0xf2, 0x7d, 0x35, 0xb5, 0x90, 0x4d, 0x7f, 0x5b, 0x4a, 0x49, 0xe4, 0xb8, 0x3b, 0xc8, 0xa1, 0x2f}}},
{{{0x8b, 0xc5, 0xcc, 0x3d, 0x69, 0xa6, 0xa1, 0x18, 0x44, 0xbc, 0x4d, 0x77, 0x37, 0xc7, 0x86, 0xec, 0x0c, 0xc9, 0xd6, 0x44, 0xa9, 0x23, 0x27, 0xb9, 0x03, 0x34, 0xa7, 0x0a, 0xd5, 0xc7, 0x34, 0x37}} ,
{{0xf9, 0x7e, 0x3e, 0x66, 0xee, 0xf9, 0x99, 0x28, 0xff, 0xad, 0x11, 0xd8, 0xe2, 0x66, 0xc5, 0xcd, 0x0f, 0x0d, 0x0b, 0x6a, 0xfc, 0x7c, 0x24, 0xa8, 0x4f, 0xa8, 0x5e, 0x80, 0x45, 0x8b, 0x6c, 0x41}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xef, 0x1e, 0xec, 0xf7, 0x8d, 0x77, 0xf2, 0xea, 0xdb, 0x60, 0x03, 0x21, 0xc0, 0xff, 0x5e, 0x67, 0xc3, 0x71, 0x0b, 0x21, 0xb4, 0x41, 0xa0, 0x68, 0x38, 0xc6, 0x01, 0xa3, 0xd3, 0x51, 0x3c, 0x3c}} ,
{{0x92, 0xf8, 0xd6, 0x4b, 0xef, 0x42, 0x13, 0xb2, 0x4a, 0xc4, 0x2e, 0x72, 0x3f, 0xc9, 0x11, 0xbd, 0x74, 0x02, 0x0e, 0xf5, 0x13, 0x9d, 0x83, 0x1a, 0x1b, 0xd5, 0x54, 0xde, 0xc4, 0x1e, 0x16, 0x6c}}},
@@ -1606,7 +1467,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x7a, 0x8d, 0xa1, 0x5d, 0x70, 0x5d, 0x51, 0x27, 0xee, 0x30, 0x65, 0x56, 0x95, 0x46, 0xde, 0xbd, 0x03, 0x75, 0xb4, 0x57, 0x59, 0x89, 0xeb, 0x02, 0x9e, 0xcc, 0x89, 0x19, 0xa7, 0xcb, 0x17, 0x67}}},
{{{0x6a, 0xeb, 0xfc, 0x9a, 0x9a, 0x10, 0xce, 0xdb, 0x3a, 0x1c, 0x3c, 0x6a, 0x9d, 0xea, 0x46, 0xbc, 0x45, 0x49, 0xac, 0xe3, 0x41, 0x12, 0x7c, 0xf0, 0xf7, 0x4f, 0xf9, 0xf7, 0xff, 0x2c, 0x89, 0x04}} ,
{{0x30, 0x31, 0x54, 0x1a, 0x46, 0xca, 0xe6, 0xc6, 0xcb, 0xe2, 0xc3, 0xc1, 0x8b, 0x75, 0x81, 0xbe, 0xee, 0xf8, 0xa3, 0x11, 0x1c, 0x25, 0xa3, 0xa7, 0x35, 0x51, 0x55, 0xe2, 0x25, 0xaa, 0xe2, 0x3a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xb4, 0x48, 0x10, 0x9f, 0x8a, 0x09, 0x76, 0xfa, 0xf0, 0x7a, 0xb0, 0x70, 0xf7, 0x83, 0x80, 0x52, 0x84, 0x2b, 0x26, 0xa2, 0xc4, 0x5d, 0x4f, 0xba, 0xb1, 0xc8, 0x40, 0x0d, 0x78, 0x97, 0xc4, 0x60}} ,
{{0xd4, 0xb1, 0x6c, 0x08, 0xc7, 0x40, 0x38, 0x73, 0x5f, 0x0b, 0xf3, 0x76, 0x5d, 0xb2, 0xa5, 0x2f, 0x57, 0x57, 0x07, 0xed, 0x08, 0xa2, 0x6c, 0x4f, 0x08, 0x02, 0xb5, 0x0e, 0xee, 0x44, 0xfa, 0x22}}},
@@ -1616,7 +1477,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xff, 0x76, 0x03, 0xc5, 0x4b, 0x89, 0x99, 0x70, 0x00, 0x59, 0x70, 0x9c, 0xd5, 0xd9, 0x11, 0x89, 0x5a, 0x46, 0xfe, 0xef, 0xdc, 0xd9, 0x55, 0x2b, 0x45, 0xa7, 0xb0, 0x2d, 0xfb, 0x24, 0xc2, 0x29}}},
{{{0x38, 0x06, 0xf8, 0x0b, 0xac, 0x82, 0xc4, 0x97, 0x2b, 0x90, 0xe0, 0xf7, 0xa8, 0xab, 0x6c, 0x08, 0x80, 0x66, 0x90, 0x46, 0xf7, 0x26, 0x2d, 0xf8, 0xf1, 0xc4, 0x6b, 0x4a, 0x82, 0x98, 0x8e, 0x37}} ,
{{0x8e, 0xb4, 0xee, 0xb8, 0xd4, 0x3f, 0xb2, 0x1b, 0xe0, 0x0a, 0x3d, 0x75, 0x34, 0x28, 0xa2, 0x8e, 0xc4, 0x92, 0x7b, 0xfe, 0x60, 0x6e, 0x6d, 0xb8, 0x31, 0x1d, 0x62, 0x0d, 0x78, 0x14, 0x42, 0x11}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x5e, 0xa8, 0xd8, 0x04, 0x9b, 0x73, 0xc9, 0xc9, 0xdc, 0x0d, 0x73, 0xbf, 0x0a, 0x0a, 0x73, 0xff, 0x18, 0x1f, 0x9c, 0x51, 0xaa, 0xc6, 0xf1, 0x83, 0x25, 0xfd, 0xab, 0xa3, 0x11, 0xd3, 0x01, 0x24}} ,
{{0x4d, 0xe3, 0x7e, 0x38, 0x62, 0x5e, 0x64, 0xbb, 0x2b, 0x53, 0xb5, 0x03, 0x68, 0xc4, 0xf2, 0x2b, 0x5a, 0x03, 0x32, 0x99, 0x4a, 0x41, 0x9a, 0xe1, 0x1a, 0xae, 0x8c, 0x48, 0xf3, 0x24, 0x32, 0x65}}},
@@ -1626,7 +1487,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x7c, 0x31, 0x33, 0xcc, 0xe4, 0xcf, 0x6c, 0xff, 0x80, 0x47, 0x77, 0xd1, 0xd8, 0xe9, 0x69, 0x97, 0x98, 0x7f, 0x20, 0x57, 0x1d, 0x1d, 0x4f, 0x08, 0x27, 0xc8, 0x35, 0x57, 0x40, 0xc6, 0x21, 0x0c}}},
{{{0xd2, 0x8e, 0x9b, 0xfa, 0x42, 0x8e, 0xdf, 0x8f, 0xc7, 0x86, 0xf9, 0xa4, 0xca, 0x70, 0x00, 0x9d, 0x21, 0xbf, 0xec, 0x57, 0x62, 0x30, 0x58, 0x8c, 0x0d, 0x35, 0xdb, 0x5d, 0x8b, 0x6a, 0xa0, 0x5a}} ,
{{0xc1, 0x58, 0x7c, 0x0d, 0x20, 0xdd, 0x11, 0x26, 0x5f, 0x89, 0x3b, 0x97, 0x58, 0xf8, 0x8b, 0xe3, 0xdf, 0x32, 0xe2, 0xfc, 0xd8, 0x67, 0xf2, 0xa5, 0x37, 0x1e, 0x6d, 0xec, 0x7c, 0x27, 0x20, 0x79}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xd0, 0xe9, 0xc0, 0xfa, 0x95, 0x45, 0x23, 0x96, 0xf1, 0x2c, 0x79, 0x25, 0x14, 0xce, 0x40, 0x14, 0x44, 0x2c, 0x36, 0x50, 0xd9, 0x63, 0x56, 0xb7, 0x56, 0x3b, 0x9e, 0xa7, 0xef, 0x89, 0xbb, 0x0e}} ,
{{0xce, 0x7f, 0xdc, 0x0a, 0xcc, 0x82, 0x1c, 0x0a, 0x78, 0x71, 0xe8, 0x74, 0x8d, 0x01, 0x30, 0x0f, 0xa7, 0x11, 0x4c, 0xdf, 0x38, 0xd7, 0xa7, 0x0d, 0xf8, 0x48, 0x52, 0x00, 0x80, 0x7b, 0x5f, 0x0e}}},
@@ -1636,7 +1497,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x50, 0x5b, 0xff, 0xa9, 0xb2, 0xf1, 0xf1, 0x78, 0xcf, 0x14, 0xa4, 0xa9, 0xfc, 0x09, 0x46, 0x94, 0x54, 0x65, 0x0d, 0x9c, 0x5f, 0x72, 0x21, 0xe2, 0x97, 0xa5, 0x2d, 0x81, 0xce, 0x4a, 0x5f, 0x79}}},
{{{0x3d, 0x5f, 0x5c, 0xd2, 0xbc, 0x7d, 0x77, 0x0e, 0x2a, 0x6d, 0x22, 0x45, 0x84, 0x06, 0xc4, 0xdd, 0xc6, 0xa6, 0xc6, 0xd7, 0x49, 0xad, 0x6d, 0x87, 0x91, 0x0e, 0x3a, 0x67, 0x1d, 0x2c, 0x1d, 0x56}} ,
{{0xfe, 0x7a, 0x74, 0xcf, 0xd4, 0xd2, 0xe5, 0x19, 0xde, 0xd0, 0xdb, 0x70, 0x23, 0x69, 0xe6, 0x6d, 0xec, 0xec, 0xcc, 0x09, 0x33, 0x6a, 0x77, 0xdc, 0x6b, 0x22, 0x76, 0x5d, 0x92, 0x09, 0xac, 0x2d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x23, 0x15, 0x17, 0xeb, 0xd3, 0xdb, 0x12, 0x5e, 0x01, 0xf0, 0x91, 0xab, 0x2c, 0x41, 0xce, 0xac, 0xed, 0x1b, 0x4b, 0x2d, 0xbc, 0xdb, 0x17, 0x66, 0x89, 0x46, 0xad, 0x4b, 0x1e, 0x6f, 0x0b, 0x14}} ,
{{0x11, 0xce, 0xbf, 0xb6, 0x77, 0x2d, 0x48, 0x22, 0x18, 0x4f, 0xa3, 0x5d, 0x4a, 0xb0, 0x70, 0x12, 0x3e, 0x54, 0xd7, 0xd8, 0x0e, 0x2b, 0x27, 0xdc, 0x53, 0xff, 0xca, 0x8c, 0x59, 0xb3, 0x4e, 0x44}}},
@@ -1646,7 +1507,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x0d, 0x16, 0xac, 0x3c, 0x4a, 0x58, 0x86, 0x3a, 0x46, 0x7f, 0x6c, 0xa3, 0x52, 0x6e, 0x37, 0xe4, 0x96, 0x9c, 0xe9, 0x5c, 0x66, 0x41, 0x67, 0xe4, 0xfb, 0x79, 0x0c, 0x05, 0xf6, 0x64, 0xd5, 0x7c}}},
{{{0x28, 0xc1, 0xe1, 0x54, 0x73, 0xf2, 0xbf, 0x76, 0x74, 0x19, 0x19, 0x1b, 0xe4, 0xb9, 0xa8, 0x46, 0x65, 0x73, 0xf3, 0x77, 0x9b, 0x29, 0x74, 0x5b, 0xc6, 0x89, 0x6c, 0x2c, 0x7c, 0xf8, 0xb3, 0x0f}} ,
{{0xf7, 0xd5, 0xe9, 0x74, 0x5d, 0xb8, 0x25, 0x16, 0xb5, 0x30, 0xbc, 0x84, 0xc5, 0xf0, 0xad, 0xca, 0x12, 0x28, 0xbc, 0x9d, 0xd4, 0xfa, 0x82, 0xe6, 0xe3, 0xbf, 0xa2, 0x15, 0x2c, 0xd4, 0x34, 0x10}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x61, 0xb1, 0x46, 0xba, 0x0e, 0x31, 0xa5, 0x67, 0x6c, 0x7f, 0xd6, 0xd9, 0x27, 0x85, 0x0f, 0x79, 0x14, 0xc8, 0x6c, 0x2f, 0x5f, 0x5b, 0x9c, 0x35, 0x3d, 0x38, 0x86, 0x77, 0x65, 0x55, 0x6a, 0x7b}} ,
{{0xd3, 0xb0, 0x3a, 0x66, 0x60, 0x1b, 0x43, 0xf1, 0x26, 0x58, 0x99, 0x09, 0x8f, 0x2d, 0xa3, 0x14, 0x71, 0x85, 0xdb, 0xed, 0xf6, 0x26, 0xd5, 0x61, 0x9a, 0x73, 0xac, 0x0e, 0xea, 0xac, 0xb7, 0x0c}}},
@@ -1656,7 +1517,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xe8, 0xc1, 0xa9, 0xc2, 0x7b, 0x59, 0x21, 0x33, 0xe2, 0x43, 0x73, 0x2b, 0xac, 0x2d, 0xc1, 0x89, 0x3b, 0x15, 0xe2, 0xd5, 0xc0, 0x97, 0x8a, 0xfd, 0x6f, 0x36, 0x33, 0xb7, 0xb9, 0xc3, 0x88, 0x09}}},
{{{0xd0, 0xb6, 0x56, 0x30, 0x5c, 0xae, 0xb3, 0x75, 0x44, 0xa4, 0x83, 0x51, 0x6e, 0x01, 0x65, 0xef, 0x45, 0x76, 0xe6, 0xf5, 0xa2, 0x0d, 0xd4, 0x16, 0x3b, 0x58, 0x2f, 0xf2, 0x2f, 0x36, 0x18, 0x3f}} ,
{{0xfd, 0x2f, 0xe0, 0x9b, 0x1e, 0x8c, 0xc5, 0x18, 0xa9, 0xca, 0xd4, 0x2b, 0x35, 0xb6, 0x95, 0x0a, 0x9f, 0x7e, 0xfb, 0xc4, 0xef, 0x88, 0x7b, 0x23, 0x43, 0xec, 0x2f, 0x0d, 0x0f, 0x7a, 0xfc, 0x5c}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x8d, 0xd2, 0xda, 0xc7, 0x44, 0xd6, 0x7a, 0xdb, 0x26, 0x7d, 0x1d, 0xb8, 0xe1, 0xde, 0x9d, 0x7a, 0x7d, 0x17, 0x7e, 0x1c, 0x37, 0x04, 0x8d, 0x2d, 0x7c, 0x5e, 0x18, 0x38, 0x1e, 0xaf, 0xc7, 0x1b}} ,
{{0x33, 0x48, 0x31, 0x00, 0x59, 0xf6, 0xf2, 0xca, 0x0f, 0x27, 0x1b, 0x63, 0x12, 0x7e, 0x02, 0x1d, 0x49, 0xc0, 0x5d, 0x79, 0x87, 0xef, 0x5e, 0x7a, 0x2f, 0x1f, 0x66, 0x55, 0xd8, 0x09, 0xd9, 0x61}}},
@@ -1666,7 +1527,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xa1, 0x4c, 0x70, 0x4b, 0x0e, 0xa0, 0x83, 0x70, 0x13, 0xa4, 0xaf, 0xb8, 0x38, 0x19, 0x22, 0x65, 0x09, 0xb4, 0x02, 0x4f, 0x06, 0xf8, 0x17, 0xce, 0x46, 0x45, 0xda, 0x50, 0x7c, 0x8a, 0xd1, 0x4e}}},
{{{0xf7, 0xd4, 0x16, 0x6c, 0x4e, 0x95, 0x9d, 0x5d, 0x0f, 0x91, 0x2b, 0x52, 0xfe, 0x5c, 0x34, 0xe5, 0x30, 0xe6, 0xa4, 0x3b, 0xf3, 0xf3, 0x34, 0x08, 0xa9, 0x4a, 0xa0, 0xb5, 0x6e, 0xb3, 0x09, 0x0a}} ,
{{0x26, 0xd9, 0x5e, 0xa3, 0x0f, 0xeb, 0xa2, 0xf3, 0x20, 0x3b, 0x37, 0xd4, 0xe4, 0x9e, 0xce, 0x06, 0x3d, 0x53, 0xed, 0xae, 0x2b, 0xeb, 0xb6, 0x24, 0x0a, 0x11, 0xa3, 0x0f, 0xd6, 0x7f, 0xa4, 0x3a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xdb, 0x9f, 0x2c, 0xfc, 0xd6, 0xb2, 0x1e, 0x2e, 0x52, 0x7a, 0x06, 0x87, 0x2d, 0x86, 0x72, 0x2b, 0x6d, 0x90, 0x77, 0x46, 0x43, 0xb5, 0x7a, 0xf8, 0x60, 0x7d, 0x91, 0x60, 0x5b, 0x9d, 0x9e, 0x07}} ,
{{0x97, 0x87, 0xc7, 0x04, 0x1c, 0x38, 0x01, 0x39, 0x58, 0xc7, 0x85, 0xa3, 0xfc, 0x64, 0x00, 0x64, 0x25, 0xa2, 0xbf, 0x50, 0x94, 0xca, 0x26, 0x31, 0x45, 0x0a, 0x24, 0xd2, 0x51, 0x29, 0x51, 0x16}}},
@@ -1676,7 +1537,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x95, 0x21, 0x01, 0xce, 0x95, 0x5b, 0x0e, 0x57, 0xc7, 0xb9, 0x62, 0xb5, 0x28, 0xca, 0x11, 0xec, 0xb4, 0x46, 0x06, 0x73, 0x26, 0xff, 0xfb, 0x66, 0x7d, 0xee, 0x5f, 0xb2, 0x56, 0xfd, 0x2a, 0x08}}},
{{{0x92, 0x67, 0x77, 0x56, 0xa1, 0xff, 0xc4, 0xc5, 0x95, 0xf0, 0xe3, 0x3a, 0x0a, 0xca, 0x94, 0x4d, 0x9e, 0x7e, 0x3d, 0xb9, 0x6e, 0xb6, 0xb0, 0xce, 0xa4, 0x30, 0x89, 0x99, 0xe9, 0xad, 0x11, 0x59}} ,
{{0xf6, 0x48, 0x95, 0xa1, 0x6f, 0x5f, 0xb7, 0xa5, 0xbb, 0x30, 0x00, 0x1c, 0xd2, 0x8a, 0xd6, 0x25, 0x26, 0x1b, 0xb2, 0x0d, 0x37, 0x6a, 0x05, 0xf4, 0x9d, 0x3e, 0x17, 0x2a, 0x43, 0xd2, 0x3a, 0x06}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x32, 0x99, 0x93, 0xd1, 0x9a, 0x72, 0xf3, 0xa9, 0x16, 0xbd, 0xb4, 0x4c, 0xdd, 0xf9, 0xd4, 0xb2, 0x64, 0x9a, 0xd3, 0x05, 0xe4, 0xa3, 0x73, 0x1c, 0xcb, 0x7e, 0x57, 0x67, 0xff, 0x04, 0xb3, 0x10}} ,
{{0xb9, 0x4b, 0xa4, 0xad, 0xd0, 0x6d, 0x61, 0x23, 0xb4, 0xaf, 0x34, 0xa9, 0xaa, 0x65, 0xec, 0xd9, 0x69, 0xe3, 0x85, 0xcd, 0xcc, 0xe7, 0xb0, 0x9b, 0x41, 0xc1, 0x1c, 0xf9, 0xa0, 0xfa, 0xb7, 0x13}}},
@@ -1686,7 +1547,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xfd, 0x4e, 0x2a, 0xc1, 0x3a, 0xca, 0x8f, 0x00, 0xd8, 0xec, 0x74, 0x67, 0xef, 0x61, 0xe0, 0x28, 0xd0, 0x96, 0xf4, 0x48, 0xde, 0x81, 0xe3, 0xef, 0xdc, 0xaa, 0x7d, 0xf3, 0xb6, 0x55, 0xa6, 0x65}}},
{{{0xeb, 0xcb, 0xc5, 0x70, 0x91, 0x31, 0x10, 0x93, 0x0d, 0xc8, 0xd0, 0xef, 0x62, 0xe8, 0x6f, 0x82, 0xe3, 0x69, 0x3d, 0x91, 0x7f, 0x31, 0xe1, 0x26, 0x35, 0x3c, 0x4a, 0x2f, 0xab, 0xc4, 0x9a, 0x5e}} ,
{{0xab, 0x1b, 0xb5, 0xe5, 0x2b, 0xc3, 0x0e, 0x29, 0xb0, 0xd0, 0x73, 0xe6, 0x4f, 0x64, 0xf2, 0xbc, 0xe4, 0xe4, 0xe1, 0x9a, 0x52, 0x33, 0x2f, 0xbd, 0xcc, 0x03, 0xee, 0x8a, 0xfa, 0x00, 0x5f, 0x50}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xf6, 0xdb, 0x0d, 0x22, 0x3d, 0xb5, 0x14, 0x75, 0x31, 0xf0, 0x81, 0xe2, 0xb9, 0x37, 0xa2, 0xa9, 0x84, 0x11, 0x9a, 0x07, 0xb5, 0x53, 0x89, 0x78, 0xa9, 0x30, 0x27, 0xa1, 0xf1, 0x4e, 0x5c, 0x2e}} ,
{{0x8b, 0x00, 0x54, 0xfb, 0x4d, 0xdc, 0xcb, 0x17, 0x35, 0x40, 0xff, 0xb7, 0x8c, 0xfe, 0x4a, 0xe4, 0x4e, 0x99, 0x4e, 0xa8, 0x74, 0x54, 0x5d, 0x5c, 0x96, 0xa3, 0x12, 0x55, 0x36, 0x31, 0x17, 0x5c}}},
@@ -1696,7 +1557,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x8c, 0x44, 0x09, 0x28, 0xd5, 0x23, 0xc9, 0x8f, 0xf3, 0x84, 0x45, 0xc6, 0x9a, 0x5e, 0xff, 0xd2, 0xc7, 0x57, 0x93, 0xa3, 0xc1, 0x69, 0xdd, 0x62, 0x0f, 0xda, 0x5c, 0x30, 0x59, 0x5d, 0xe9, 0x4c}}},
{{{0x92, 0x7e, 0x50, 0x27, 0x72, 0xd7, 0x0c, 0xd6, 0x69, 0x96, 0x81, 0x35, 0x84, 0x94, 0x35, 0x8b, 0x6c, 0xaa, 0x62, 0x86, 0x6e, 0x1c, 0x15, 0xf3, 0x6c, 0xb3, 0xff, 0x65, 0x1b, 0xa2, 0x9b, 0x59}} ,
{{0xe2, 0xa9, 0x65, 0x88, 0xc4, 0x50, 0xfa, 0xbb, 0x3b, 0x6e, 0x5f, 0x44, 0x01, 0xca, 0x97, 0xd4, 0xdd, 0xf6, 0xcd, 0x3f, 0x3f, 0xe5, 0x97, 0x67, 0x2b, 0x8c, 0x66, 0x0f, 0x35, 0x9b, 0xf5, 0x07}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xf1, 0x59, 0x27, 0xd8, 0xdb, 0x5a, 0x11, 0x5e, 0x82, 0xf3, 0x38, 0xff, 0x1c, 0xed, 0xfe, 0x3f, 0x64, 0x54, 0x3f, 0x7f, 0xd1, 0x81, 0xed, 0xef, 0x65, 0xc5, 0xcb, 0xfd, 0xe1, 0x80, 0xcd, 0x11}} ,
{{0xe0, 0xdb, 0x22, 0x28, 0xe6, 0xff, 0x61, 0x9d, 0x41, 0x14, 0x2d, 0x3b, 0x26, 0x22, 0xdf, 0xf1, 0x34, 0x81, 0xe9, 0x45, 0xee, 0x0f, 0x98, 0x8b, 0xa6, 0x3f, 0xef, 0xf7, 0x43, 0x19, 0xf1, 0x43}}},
@@ -1706,7 +1567,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x60, 0xbc, 0x45, 0x1f, 0x23, 0xa2, 0x3b, 0x70, 0x76, 0xe6, 0x97, 0x99, 0x4f, 0x77, 0x54, 0x67, 0x30, 0x9a, 0xe7, 0x66, 0xd6, 0xcd, 0x2e, 0x51, 0x24, 0x2c, 0x42, 0x4a, 0x11, 0xfe, 0x6f, 0x7e}}},
{{{0x87, 0xc0, 0xb1, 0xf0, 0xa3, 0x6f, 0x0c, 0x93, 0xa9, 0x0a, 0x72, 0xef, 0x5c, 0xbe, 0x65, 0x35, 0xa7, 0x6a, 0x4e, 0x2c, 0xbf, 0x21, 0x23, 0xe8, 0x2f, 0x97, 0xc7, 0x3e, 0xc8, 0x17, 0xac, 0x1e}} ,
{{0x7b, 0xef, 0x21, 0xe5, 0x40, 0xcc, 0x1e, 0xdc, 0xd6, 0xbd, 0x97, 0x7a, 0x7c, 0x75, 0x86, 0x7a, 0x25, 0x5a, 0x6e, 0x7c, 0xe5, 0x51, 0x3c, 0x1b, 0x5b, 0x82, 0x9a, 0x07, 0x60, 0xa1, 0x19, 0x04}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x96, 0x88, 0xa6, 0xab, 0x8f, 0xe3, 0x3a, 0x49, 0xf8, 0xfe, 0x34, 0xe7, 0x6a, 0xb2, 0xfe, 0x40, 0x26, 0x74, 0x57, 0x4c, 0xf6, 0xd4, 0x99, 0xce, 0x5d, 0x7b, 0x2f, 0x67, 0xd6, 0x5a, 0xe4, 0x4e}} ,
{{0x5c, 0x82, 0xb3, 0xbd, 0x55, 0x25, 0xf6, 0x6a, 0x93, 0xa4, 0x02, 0xc6, 0x7d, 0x5c, 0xb1, 0x2b, 0x5b, 0xff, 0xfb, 0x56, 0xf8, 0x01, 0x41, 0x90, 0xc6, 0xb6, 0xac, 0x4f, 0xfe, 0xa7, 0x41, 0x70}}},
@@ -1716,7 +1577,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x17, 0x0f, 0x86, 0x52, 0xd7, 0x9d, 0xc3, 0x44, 0x51, 0x76, 0x32, 0x65, 0xb4, 0x37, 0x81, 0x99, 0x46, 0x37, 0x62, 0xed, 0xcf, 0x64, 0x9d, 0x72, 0x40, 0x7a, 0x4c, 0x0b, 0x76, 0x2a, 0xfb, 0x56}}},
{{{0x33, 0xa7, 0x90, 0x7c, 0xc3, 0x6f, 0x17, 0xa5, 0xa0, 0x67, 0x72, 0x17, 0xea, 0x7e, 0x63, 0x14, 0x83, 0xde, 0xc1, 0x71, 0x2d, 0x41, 0x32, 0x7a, 0xf3, 0xd1, 0x2b, 0xd8, 0x2a, 0xa6, 0x46, 0x36}} ,
{{0xac, 0xcc, 0x6b, 0x7c, 0xf9, 0xb8, 0x8b, 0x08, 0x5c, 0xd0, 0x7d, 0x8f, 0x73, 0xea, 0x20, 0xda, 0x86, 0xca, 0x00, 0xc7, 0xad, 0x73, 0x4d, 0xe9, 0xe8, 0xa9, 0xda, 0x1f, 0x03, 0x06, 0xdd, 0x24}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x9c, 0xb2, 0x61, 0x0a, 0x98, 0x2a, 0xa5, 0xd7, 0xee, 0xa9, 0xac, 0x65, 0xcb, 0x0a, 0x1e, 0xe2, 0xbe, 0xdc, 0x85, 0x59, 0x0f, 0x9c, 0xa6, 0x57, 0x34, 0xa5, 0x87, 0xeb, 0x7b, 0x1e, 0x0c, 0x3c}} ,
{{0x2f, 0xbd, 0x84, 0x63, 0x0d, 0xb5, 0xa0, 0xf0, 0x4b, 0x9e, 0x93, 0xc6, 0x34, 0x9a, 0x34, 0xff, 0x73, 0x19, 0x2f, 0x6e, 0x54, 0x45, 0x2c, 0x92, 0x31, 0x76, 0x34, 0xf1, 0xb2, 0x26, 0xe8, 0x74}}},
@@ -1726,7 +1587,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x87, 0xc8, 0xa0, 0x6e, 0xe1, 0xb0, 0xad, 0x6a, 0x4a, 0x34, 0x71, 0xed, 0x7c, 0xd6, 0x44, 0x03, 0x65, 0x4a, 0x5c, 0x5c, 0x04, 0xf5, 0x24, 0x3f, 0xb0, 0x16, 0x5e, 0x8c, 0xb2, 0xd2, 0xc5, 0x20}}},
{{{0x98, 0x83, 0xc2, 0x37, 0xa0, 0x41, 0xa8, 0x48, 0x5c, 0x5f, 0xbf, 0xc8, 0xfa, 0x24, 0xe0, 0x59, 0x2c, 0xbd, 0xf6, 0x81, 0x7e, 0x88, 0xe6, 0xca, 0x04, 0xd8, 0x5d, 0x60, 0xbb, 0x74, 0xa7, 0x0b}} ,
{{0x21, 0x13, 0x91, 0xbf, 0x77, 0x7a, 0x33, 0xbc, 0xe9, 0x07, 0x39, 0x0a, 0xdd, 0x7d, 0x06, 0x10, 0x9a, 0xee, 0x47, 0x73, 0x1b, 0x15, 0x5a, 0xfb, 0xcd, 0x4d, 0xd0, 0xd2, 0x3a, 0x01, 0xba, 0x54}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x48, 0xd5, 0x39, 0x4a, 0x0b, 0x20, 0x6a, 0x43, 0xa0, 0x07, 0x82, 0x5e, 0x49, 0x7c, 0xc9, 0x47, 0xf1, 0x7c, 0x37, 0xb9, 0x23, 0xef, 0x6b, 0x46, 0x45, 0x8c, 0x45, 0x76, 0xdf, 0x14, 0x6b, 0x6e}} ,
{{0x42, 0xc9, 0xca, 0x29, 0x4c, 0x76, 0x37, 0xda, 0x8a, 0x2d, 0x7c, 0x3a, 0x58, 0xf2, 0x03, 0xb4, 0xb5, 0xb9, 0x1a, 0x13, 0x2d, 0xde, 0x5f, 0x6b, 0x9d, 0xba, 0x52, 0xc9, 0x5d, 0xb3, 0xf3, 0x30}}},
@@ -1736,7 +1597,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xe4, 0xe7, 0x43, 0x4b, 0xa0, 0x3f, 0x2b, 0x06, 0xba, 0x17, 0xae, 0x3d, 0xe6, 0xce, 0xbd, 0xb8, 0xed, 0x74, 0x11, 0x35, 0xec, 0x96, 0xfe, 0x31, 0xe3, 0x0e, 0x7a, 0x4e, 0xc9, 0x1d, 0xcb, 0x20}}},
{{{0xe0, 0x67, 0xe9, 0x7b, 0xdb, 0x96, 0x5c, 0xb0, 0x32, 0xd0, 0x59, 0x31, 0x90, 0xdc, 0x92, 0x97, 0xac, 0x09, 0x38, 0x31, 0x0f, 0x7e, 0xd6, 0x5d, 0xd0, 0x06, 0xb6, 0x1f, 0xea, 0xf0, 0x5b, 0x07}} ,
{{0x81, 0x9f, 0xc7, 0xde, 0x6b, 0x41, 0x22, 0x35, 0x14, 0x67, 0x77, 0x3e, 0x90, 0x81, 0xb0, 0xd9, 0x85, 0x4c, 0xca, 0x9b, 0x3f, 0x04, 0x59, 0xd6, 0xaa, 0x17, 0xc3, 0x88, 0x34, 0x37, 0xba, 0x43}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x4c, 0xb6, 0x69, 0xc8, 0x81, 0x95, 0x94, 0x33, 0x92, 0x34, 0xe9, 0x3c, 0x84, 0x0d, 0x3d, 0x5a, 0x37, 0x9c, 0x22, 0xa0, 0xaa, 0x65, 0xce, 0xb4, 0xc2, 0x2d, 0x66, 0x67, 0x02, 0xff, 0x74, 0x10}} ,
{{0x22, 0xb0, 0xd5, 0xe6, 0xc7, 0xef, 0xb1, 0xa7, 0x13, 0xda, 0x60, 0xb4, 0x80, 0xc1, 0x42, 0x7d, 0x10, 0x70, 0x97, 0x04, 0x4d, 0xda, 0x23, 0x89, 0xc2, 0x0e, 0x68, 0xcb, 0xde, 0xe0, 0x9b, 0x29}}},
@@ -1746,7 +1607,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xa0, 0x20, 0x99, 0x69, 0x0a, 0xae, 0xa3, 0x70, 0x4e, 0x64, 0x80, 0xb7, 0x85, 0x9c, 0x87, 0x54, 0x43, 0x43, 0x55, 0x80, 0x6d, 0x8d, 0x7c, 0xa9, 0x64, 0xca, 0x6c, 0x2e, 0x21, 0xd8, 0xc8, 0x6c}}},
{{{0x91, 0x4a, 0x07, 0xad, 0x08, 0x75, 0xc1, 0x4f, 0xa4, 0xb2, 0xc3, 0x6f, 0x46, 0x3e, 0xb1, 0xce, 0x52, 0xab, 0x67, 0x09, 0x54, 0x48, 0x6b, 0x6c, 0xd7, 0x1d, 0x71, 0x76, 0xcb, 0xff, 0xdd, 0x31}} ,
{{0x36, 0x88, 0xfa, 0xfd, 0xf0, 0x36, 0x6f, 0x07, 0x74, 0x88, 0x50, 0xd0, 0x95, 0x38, 0x4a, 0x48, 0x2e, 0x07, 0x64, 0x97, 0x11, 0x76, 0x01, 0x1a, 0x27, 0x4d, 0x8e, 0x25, 0x9a, 0x9b, 0x1c, 0x22}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xbe, 0x57, 0xbd, 0x0e, 0x0f, 0xac, 0x5e, 0x76, 0xa3, 0x71, 0xad, 0x2b, 0x10, 0x45, 0x02, 0xec, 0x59, 0xd5, 0x5d, 0xa9, 0x44, 0xcc, 0x25, 0x4c, 0xb3, 0x3c, 0x5b, 0x69, 0x07, 0x55, 0x26, 0x6b}} ,
{{0x30, 0x6b, 0xd4, 0xa7, 0x51, 0x29, 0xe3, 0xf9, 0x7a, 0x75, 0x2a, 0x82, 0x2f, 0xd6, 0x1d, 0x99, 0x2b, 0x80, 0xd5, 0x67, 0x1e, 0x15, 0x9d, 0xca, 0xfd, 0xeb, 0xac, 0x97, 0x35, 0x09, 0x7f, 0x3f}}},
@@ -1756,7 +1617,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xad, 0xa0, 0x41, 0xec, 0xc8, 0x4d, 0xb9, 0xd2, 0x6e, 0x96, 0x4e, 0x5b, 0xc5, 0xc2, 0xa0, 0x1b, 0xcf, 0x0c, 0xbf, 0x17, 0x66, 0x57, 0xc1, 0x17, 0x90, 0x45, 0x71, 0xc2, 0xe1, 0x24, 0xeb, 0x27}}},
{{{0x2c, 0xb9, 0x42, 0xa4, 0xaf, 0x3b, 0x42, 0x0e, 0xc2, 0x0f, 0xf2, 0xea, 0x83, 0xaf, 0x9a, 0x13, 0x17, 0xb0, 0xbd, 0x89, 0x17, 0xe3, 0x72, 0xcb, 0x0e, 0x76, 0x7e, 0x41, 0x63, 0x04, 0x88, 0x71}} ,
{{0x75, 0x78, 0x38, 0x86, 0x57, 0xdd, 0x9f, 0xee, 0x54, 0x70, 0x65, 0xbf, 0xf1, 0x2c, 0xe0, 0x39, 0x0d, 0xe3, 0x89, 0xfd, 0x8e, 0x93, 0x4f, 0x43, 0xdc, 0xd5, 0x5b, 0xde, 0xf9, 0x98, 0xe5, 0x7b}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xe7, 0x3b, 0x65, 0x11, 0xdf, 0xb2, 0xf2, 0x63, 0x94, 0x12, 0x6f, 0x5c, 0x9e, 0x77, 0xc1, 0xb6, 0xd8, 0xab, 0x58, 0x7a, 0x1d, 0x95, 0x73, 0xdd, 0xe7, 0xe3, 0x6f, 0xf2, 0x03, 0x1d, 0xdb, 0x76}} ,
{{0xae, 0x06, 0x4e, 0x2c, 0x52, 0x1b, 0xbc, 0x5a, 0x5a, 0xa5, 0xbe, 0x27, 0xbd, 0xeb, 0xe1, 0x14, 0x17, 0x68, 0x26, 0x07, 0x03, 0xd1, 0x18, 0x0b, 0xdf, 0xf1, 0x06, 0x5c, 0xa6, 0x1b, 0xb9, 0x24}}},
@@ -1766,7 +1627,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x8d, 0xbd, 0xa3, 0xc4, 0x06, 0x9b, 0x1f, 0x90, 0x58, 0x60, 0x74, 0xb2, 0x00, 0x3b, 0x3c, 0xd2, 0xda, 0x82, 0xbb, 0x10, 0x90, 0x69, 0x92, 0xa9, 0xb4, 0x30, 0x81, 0xe3, 0x7c, 0xa8, 0x89, 0x45}}},
{{{0x3f, 0xdc, 0x05, 0xcb, 0x41, 0x3c, 0xc8, 0x23, 0x04, 0x2c, 0x38, 0x99, 0xe3, 0x68, 0x55, 0xf9, 0xd3, 0x32, 0xc7, 0xbf, 0xfa, 0xd4, 0x1b, 0x5d, 0xde, 0xdc, 0x10, 0x42, 0xc0, 0x42, 0xd9, 0x75}} ,
{{0x2d, 0xab, 0x35, 0x4e, 0x87, 0xc4, 0x65, 0x97, 0x67, 0x24, 0xa4, 0x47, 0xad, 0x3f, 0x8e, 0xf3, 0xcb, 0x31, 0x17, 0x77, 0xc5, 0xe2, 0xd7, 0x8f, 0x3c, 0xc1, 0xcd, 0x56, 0x48, 0xc1, 0x6c, 0x69}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x14, 0xae, 0x5f, 0x88, 0x7b, 0xa5, 0x90, 0xdf, 0x10, 0xb2, 0x8b, 0x5e, 0x24, 0x17, 0xc3, 0xa3, 0xd4, 0x0f, 0x92, 0x61, 0x1a, 0x19, 0x5a, 0xad, 0x76, 0xbd, 0xd8, 0x1c, 0xdd, 0xe0, 0x12, 0x6d}} ,
{{0x8e, 0xbd, 0x70, 0x8f, 0x02, 0xa3, 0x24, 0x4d, 0x5a, 0x67, 0xc4, 0xda, 0xf7, 0x20, 0x0f, 0x81, 0x5b, 0x7a, 0x05, 0x24, 0x67, 0x83, 0x0b, 0x2a, 0x80, 0xe7, 0xfd, 0x74, 0x4b, 0x9e, 0x5c, 0x0d}}},
@@ -1776,7 +1637,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x97, 0x29, 0x6c, 0xc4, 0x42, 0x0b, 0xdd, 0xc0, 0x29, 0x5c, 0x9b, 0x34, 0x97, 0xd0, 0xc7, 0x79, 0x80, 0x63, 0x74, 0xe4, 0x8e, 0x37, 0xb0, 0x2b, 0x7c, 0xe8, 0x68, 0x6c, 0xc3, 0x82, 0x97, 0x57}}},
{{{0x22, 0xbe, 0x83, 0xb6, 0x4b, 0x80, 0x6b, 0x43, 0x24, 0x5e, 0xef, 0x99, 0x9b, 0xa8, 0xfc, 0x25, 0x8d, 0x3b, 0x03, 0x94, 0x2b, 0x3e, 0xe7, 0x95, 0x76, 0x9b, 0xcc, 0x15, 0xdb, 0x32, 0xe6, 0x66}} ,
{{0x84, 0xf0, 0x4a, 0x13, 0xa6, 0xd6, 0xfa, 0x93, 0x46, 0x07, 0xf6, 0x7e, 0x5c, 0x6d, 0x5e, 0xf6, 0xa6, 0xe7, 0x48, 0xf0, 0x06, 0xea, 0xff, 0x90, 0xc1, 0xcc, 0x4c, 0x19, 0x9c, 0x3c, 0x4e, 0x53}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x2a, 0x50, 0xe3, 0x07, 0x15, 0x59, 0xf2, 0x8b, 0x81, 0xf2, 0xf3, 0xd3, 0x6c, 0x99, 0x8c, 0x70, 0x67, 0xec, 0xcc, 0xee, 0x9e, 0x59, 0x45, 0x59, 0x7d, 0x47, 0x75, 0x69, 0xf5, 0x24, 0x93, 0x5d}} ,
{{0x6a, 0x4f, 0x1b, 0xbe, 0x6b, 0x30, 0xcf, 0x75, 0x46, 0xe3, 0x7b, 0x9d, 0xfc, 0xcd, 0xd8, 0x5c, 0x1f, 0xb4, 0xc8, 0xe2, 0x24, 0xec, 0x1a, 0x28, 0x05, 0x32, 0x57, 0xfd, 0x3c, 0x5a, 0x98, 0x10}}},
@@ -1786,7 +1647,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x9b, 0x35, 0xe2, 0xa9, 0x3d, 0x32, 0x1e, 0xbb, 0x16, 0x28, 0x70, 0xe9, 0x45, 0x2f, 0x8f, 0x70, 0x7f, 0x08, 0x7e, 0x53, 0xc4, 0x7a, 0xbf, 0xf7, 0xe1, 0xa4, 0x6a, 0xd8, 0xac, 0x64, 0x1b, 0x11}}},
{{{0xb2, 0xeb, 0x47, 0x46, 0x18, 0x3e, 0x1f, 0x99, 0x0c, 0xcc, 0xf1, 0x2c, 0xe0, 0xe7, 0x8f, 0xe0, 0x01, 0x7e, 0x65, 0xb8, 0x0c, 0xd0, 0xfb, 0xc8, 0xb9, 0x90, 0x98, 0x33, 0x61, 0x3b, 0xd8, 0x27}} ,
{{0xa0, 0xbe, 0x72, 0x3a, 0x50, 0x4b, 0x74, 0xab, 0x01, 0xc8, 0x93, 0xc5, 0xe4, 0xc7, 0x08, 0x6c, 0xb4, 0xca, 0xee, 0xeb, 0x8e, 0xd7, 0x4e, 0x26, 0xc6, 0x1d, 0xe2, 0x71, 0xaf, 0x89, 0xa0, 0x2a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x98, 0x0b, 0xe4, 0xde, 0xdb, 0xa8, 0xfa, 0x82, 0x74, 0x06, 0x52, 0x6d, 0x08, 0x52, 0x8a, 0xff, 0x62, 0xc5, 0x6a, 0x44, 0x0f, 0x51, 0x8c, 0x1f, 0x6e, 0xb6, 0xc6, 0x2c, 0x81, 0xd3, 0x76, 0x46}} ,
{{0xf4, 0x29, 0x74, 0x2e, 0x80, 0xa7, 0x1a, 0x8f, 0xf6, 0xbd, 0xd6, 0x8e, 0xbf, 0xc1, 0x95, 0x2a, 0xeb, 0xa0, 0x7f, 0x45, 0xa0, 0x50, 0x14, 0x05, 0xb1, 0x57, 0x4c, 0x74, 0xb7, 0xe2, 0x89, 0x7d}}},
@@ -1796,7 +1657,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x29, 0xd8, 0xf2, 0xaa, 0xe9, 0x0e, 0xf7, 0x2e, 0x5f, 0x9d, 0x8a, 0x5b, 0x09, 0xed, 0xc9, 0x24, 0x22, 0xf4, 0x0f, 0x25, 0x8f, 0x1c, 0x84, 0x6e, 0x34, 0x14, 0x6c, 0xea, 0xb3, 0x86, 0x5d, 0x04}}},
{{{0x07, 0x98, 0x61, 0xe8, 0x6a, 0xd2, 0x81, 0x49, 0x25, 0xd5, 0x5b, 0x18, 0xc7, 0x35, 0x52, 0x51, 0xa4, 0x46, 0xad, 0x18, 0x0d, 0xc9, 0x5f, 0x18, 0x91, 0x3b, 0xb4, 0xc0, 0x60, 0x59, 0x8d, 0x66}} ,
{{0x03, 0x1b, 0x79, 0x53, 0x6e, 0x24, 0xae, 0x57, 0xd9, 0x58, 0x09, 0x85, 0x48, 0xa2, 0xd3, 0xb5, 0xe2, 0x4d, 0x11, 0x82, 0xe6, 0x86, 0x3c, 0xe9, 0xb1, 0x00, 0x19, 0xc2, 0x57, 0xf7, 0x66, 0x7a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x0f, 0xe3, 0x89, 0x03, 0xd7, 0x22, 0x95, 0x9f, 0xca, 0xb4, 0x8d, 0x9e, 0x6d, 0x97, 0xff, 0x8d, 0x21, 0x59, 0x07, 0xef, 0x03, 0x2d, 0x5e, 0xf8, 0x44, 0x46, 0xe7, 0x85, 0x80, 0xc5, 0x89, 0x50}} ,
{{0x8b, 0xd8, 0x53, 0x86, 0x24, 0x86, 0x29, 0x52, 0x01, 0xfa, 0x20, 0xc3, 0x4e, 0x95, 0xcb, 0xad, 0x7b, 0x34, 0x94, 0x30, 0xb7, 0x7a, 0xfa, 0x96, 0x41, 0x60, 0x2b, 0xcb, 0x59, 0xb9, 0xca, 0x50}}},
@@ -1806,7 +1667,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x19, 0xf9, 0x35, 0xaa, 0x59, 0x1a, 0x0c, 0x5c, 0x06, 0xfc, 0x6a, 0x0b, 0x97, 0x53, 0x36, 0xfc, 0x2a, 0xa5, 0x5a, 0x9b, 0x30, 0xef, 0x23, 0xaf, 0x39, 0x5d, 0x9a, 0x6b, 0x75, 0x57, 0x48, 0x0b}}},
{{{0x26, 0xdc, 0x76, 0x3b, 0xfc, 0xf9, 0x9c, 0x3f, 0x89, 0x0b, 0x62, 0x53, 0xaf, 0x83, 0x01, 0x2e, 0xbc, 0x6a, 0xc6, 0x03, 0x0d, 0x75, 0x2a, 0x0d, 0xe6, 0x94, 0x54, 0xcf, 0xb3, 0xe5, 0x96, 0x25}} ,
{{0xfe, 0x82, 0xb1, 0x74, 0x31, 0x8a, 0xa7, 0x6f, 0x56, 0xbd, 0x8d, 0xf4, 0xe0, 0x94, 0x51, 0x59, 0xde, 0x2c, 0x5a, 0xf4, 0x84, 0x6b, 0x4a, 0x88, 0x93, 0xc0, 0x0c, 0x9a, 0xac, 0xa7, 0xa0, 0x68}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x25, 0x0d, 0xd6, 0xc7, 0x23, 0x47, 0x10, 0xad, 0xc7, 0x08, 0x5c, 0x87, 0x87, 0x93, 0x98, 0x18, 0xb8, 0xd3, 0x9c, 0xac, 0x5a, 0x3d, 0xc5, 0x75, 0xf8, 0x49, 0x32, 0x14, 0xcc, 0x51, 0x96, 0x24}} ,
{{0x65, 0x9c, 0x5d, 0xf0, 0x37, 0x04, 0xf0, 0x34, 0x69, 0x2a, 0xf0, 0xa5, 0x64, 0xca, 0xde, 0x2b, 0x5b, 0x15, 0x10, 0xd2, 0xab, 0x06, 0xdd, 0xc4, 0xb0, 0xb6, 0x5b, 0xc1, 0x17, 0xdf, 0x8f, 0x02}}},
@@ -1816,7 +1677,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x00, 0xa9, 0x4c, 0xb2, 0x12, 0xf8, 0x32, 0xa8, 0x7a, 0x00, 0x4b, 0x49, 0x32, 0xba, 0x1f, 0x5d, 0x44, 0x8e, 0x44, 0x7a, 0xdc, 0x11, 0xfb, 0x39, 0x08, 0x57, 0x87, 0xa5, 0x12, 0x42, 0x93, 0x0e}}},
{{{0x17, 0xb4, 0xae, 0x72, 0x59, 0xd0, 0xaa, 0xa8, 0x16, 0x8b, 0x63, 0x11, 0xb3, 0x43, 0x04, 0xda, 0x0c, 0xa8, 0xb7, 0x68, 0xdd, 0x4e, 0x54, 0xe7, 0xaf, 0x5d, 0x5d, 0x05, 0x76, 0x36, 0xec, 0x0d}} ,
{{0x6d, 0x7c, 0x82, 0x32, 0x38, 0x55, 0x57, 0x74, 0x5b, 0x7d, 0xc3, 0xc4, 0xfb, 0x06, 0x29, 0xf0, 0x13, 0x55, 0x54, 0xc6, 0xa7, 0xdc, 0x4c, 0x9f, 0x98, 0x49, 0x20, 0xa8, 0xc3, 0x8d, 0xfa, 0x48}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x87, 0x47, 0x9d, 0xe9, 0x25, 0xd5, 0xe3, 0x47, 0x78, 0xdf, 0x85, 0xa7, 0x85, 0x5e, 0x7a, 0x4c, 0x5f, 0x79, 0x1a, 0xf3, 0xa2, 0xb2, 0x28, 0xa0, 0x9c, 0xdd, 0x30, 0x40, 0xd4, 0x38, 0xbd, 0x28}} ,
{{0xfc, 0xbb, 0xd5, 0x78, 0x6d, 0x1d, 0xd4, 0x99, 0xb4, 0xaa, 0x44, 0x44, 0x7a, 0x1b, 0xd8, 0xfe, 0xb4, 0x99, 0xb9, 0xcc, 0xe7, 0xc4, 0xd3, 0x3a, 0x73, 0x83, 0x41, 0x5c, 0x40, 0xd7, 0x2d, 0x55}}},
@@ -1826,7 +1687,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xc0, 0x61, 0x48, 0x48, 0x17, 0xf4, 0x9e, 0x18, 0x51, 0x2d, 0xea, 0x2f, 0xf2, 0xf2, 0xe0, 0xa3, 0x14, 0xb7, 0x8b, 0x3a, 0x30, 0xf5, 0x81, 0xc1, 0x5d, 0x71, 0x39, 0x62, 0x55, 0x1f, 0x60, 0x5a}}},
{{{0xe5, 0x89, 0x8a, 0x76, 0x6c, 0xdb, 0x4d, 0x0a, 0x5b, 0x72, 0x9d, 0x59, 0x6e, 0x63, 0x63, 0x18, 0x7c, 0xe3, 0xfa, 0xe2, 0xdb, 0xa1, 0x8d, 0xf4, 0xa5, 0xd7, 0x16, 0xb2, 0xd0, 0xb3, 0x3f, 0x39}} ,
{{0xce, 0x60, 0x09, 0x6c, 0xf5, 0x76, 0x17, 0x24, 0x80, 0x3a, 0x96, 0xc7, 0x94, 0x2e, 0xf7, 0x6b, 0xef, 0xb5, 0x05, 0x96, 0xef, 0xd3, 0x7b, 0x51, 0xda, 0x05, 0x44, 0x67, 0xbc, 0x07, 0x21, 0x4e}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xe9, 0x73, 0x6f, 0x21, 0xb9, 0xde, 0x22, 0x7d, 0xeb, 0x97, 0x31, 0x10, 0xa3, 0xea, 0xe1, 0xc6, 0x37, 0xeb, 0x8f, 0x43, 0x58, 0xde, 0x41, 0x64, 0x0e, 0x3e, 0x07, 0x99, 0x3d, 0xf1, 0xdf, 0x1e}} ,
{{0xf8, 0xad, 0x43, 0xc2, 0x17, 0x06, 0xe2, 0xe4, 0xa9, 0x86, 0xcd, 0x18, 0xd7, 0x78, 0xc8, 0x74, 0x66, 0xd2, 0x09, 0x18, 0xa5, 0xf1, 0xca, 0xa6, 0x62, 0x92, 0xc1, 0xcb, 0x00, 0xeb, 0x42, 0x2e}}},
@@ -1836,7 +1697,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xa3, 0x64, 0x17, 0x9b, 0x8b, 0xc8, 0x3a, 0x61, 0xe6, 0x9d, 0xc6, 0xed, 0x7b, 0x03, 0x52, 0x26, 0x9d, 0x3a, 0xb3, 0x13, 0xcc, 0x8a, 0xfd, 0x2c, 0x1a, 0x1d, 0xed, 0x13, 0xd0, 0x55, 0x57, 0x0e}}},
{{{0x1a, 0xea, 0xbf, 0xfd, 0x4a, 0x3c, 0x8e, 0xec, 0x29, 0x7e, 0x77, 0x77, 0x12, 0x99, 0xd7, 0x84, 0xf9, 0x55, 0x7f, 0xf1, 0x8b, 0xb4, 0xd2, 0x95, 0xa3, 0x8d, 0xf0, 0x8a, 0xa7, 0xeb, 0x82, 0x4b}} ,
{{0x2c, 0x28, 0xf4, 0x3a, 0xf6, 0xde, 0x0a, 0xe0, 0x41, 0x44, 0x23, 0xf8, 0x3f, 0x03, 0x64, 0x9f, 0xc3, 0x55, 0x4c, 0xc6, 0xc1, 0x94, 0x1c, 0x24, 0x5d, 0x5f, 0x92, 0x45, 0x96, 0x57, 0x37, 0x14}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xc1, 0xcd, 0x90, 0x66, 0xb9, 0x76, 0xa0, 0x5b, 0xa5, 0x85, 0x75, 0x23, 0xf9, 0x89, 0xa5, 0x82, 0xb2, 0x6f, 0xb1, 0xeb, 0xc4, 0x69, 0x6f, 0x18, 0x5a, 0xed, 0x94, 0x3d, 0x9d, 0xd9, 0x2c, 0x1a}} ,
{{0x35, 0xb0, 0xe6, 0x73, 0x06, 0xb7, 0x37, 0xe0, 0xf8, 0xb0, 0x22, 0xe8, 0xd2, 0xed, 0x0b, 0xef, 0xe6, 0xc6, 0x5a, 0x99, 0x9e, 0x1a, 0x9f, 0x04, 0x97, 0xe4, 0x4d, 0x0b, 0xbe, 0xba, 0x44, 0x40}}},
@@ -1846,7 +1707,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xa7, 0xbb, 0xf6, 0x8e, 0xad, 0xdd, 0xf7, 0x90, 0xdd, 0x5f, 0x93, 0x89, 0xae, 0x04, 0x37, 0xe6, 0x9a, 0xb7, 0xe8, 0xc0, 0xdf, 0x16, 0x2a, 0xbf, 0xc4, 0x3a, 0x3c, 0x41, 0xd5, 0x89, 0x72, 0x5a}}},
{{{0x1f, 0x96, 0xff, 0x34, 0x2c, 0x13, 0x21, 0xcb, 0x0a, 0x89, 0x85, 0xbe, 0xb3, 0x70, 0x9e, 0x1e, 0xde, 0x97, 0xaf, 0x96, 0x30, 0xf7, 0x48, 0x89, 0x40, 0x8d, 0x07, 0xf1, 0x25, 0xf0, 0x30, 0x58}} ,
{{0x1e, 0xd4, 0x93, 0x57, 0xe2, 0x17, 0xe7, 0x9d, 0xab, 0x3c, 0x55, 0x03, 0x82, 0x2f, 0x2b, 0xdb, 0x56, 0x1e, 0x30, 0x2e, 0x24, 0x47, 0x6e, 0xe6, 0xff, 0x33, 0x24, 0x2c, 0x75, 0x51, 0xd4, 0x67}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x2b, 0x06, 0xd9, 0xa1, 0x5d, 0xe1, 0xf4, 0xd1, 0x1e, 0x3c, 0x9a, 0xc6, 0x29, 0x2b, 0x13, 0x13, 0x78, 0xc0, 0xd8, 0x16, 0x17, 0x2d, 0x9e, 0xa9, 0xc9, 0x79, 0x57, 0xab, 0x24, 0x91, 0x92, 0x19}} ,
{{0x69, 0xfb, 0xa1, 0x9c, 0xa6, 0x75, 0x49, 0x7d, 0x60, 0x73, 0x40, 0x42, 0xc4, 0x13, 0x0a, 0x95, 0x79, 0x1e, 0x04, 0x83, 0x94, 0x99, 0x9b, 0x1e, 0x0c, 0xe8, 0x1f, 0x54, 0xef, 0xcb, 0xc0, 0x52}}},
@@ -1856,7 +1717,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x9d, 0x45, 0xd6, 0x80, 0xe6, 0x45, 0xaa, 0xf4, 0x15, 0xaa, 0x5c, 0x34, 0x87, 0x99, 0xa2, 0x8c, 0x26, 0x84, 0x62, 0x7d, 0xb6, 0x29, 0xc0, 0x52, 0xea, 0xf5, 0x81, 0x18, 0x0f, 0x35, 0xa9, 0x0e}}},
{{{0xe7, 0x20, 0x72, 0x7c, 0x6d, 0x94, 0x5f, 0x52, 0x44, 0x54, 0xe3, 0xf1, 0xb2, 0xb0, 0x36, 0x46, 0x0f, 0xae, 0x92, 0xe8, 0x70, 0x9d, 0x6e, 0x79, 0xb1, 0xad, 0x37, 0xa9, 0x5f, 0xc0, 0xde, 0x03}} ,
{{0x15, 0x55, 0x37, 0xc6, 0x1c, 0x27, 0x1c, 0x6d, 0x14, 0x4f, 0xca, 0xa4, 0xc4, 0x88, 0x25, 0x46, 0x39, 0xfc, 0x5a, 0xe5, 0xfe, 0x29, 0x11, 0x69, 0xf5, 0x72, 0x84, 0x4d, 0x78, 0x9f, 0x94, 0x15}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xec, 0xd3, 0xff, 0x57, 0x0b, 0xb0, 0xb2, 0xdc, 0xf8, 0x4f, 0xe2, 0x12, 0xd5, 0x36, 0xbe, 0x6b, 0x09, 0x43, 0x6d, 0xa3, 0x4d, 0x90, 0x2d, 0xb8, 0x74, 0xe8, 0x71, 0x45, 0x19, 0x8b, 0x0c, 0x6a}} ,
{{0xb8, 0x42, 0x1c, 0x03, 0xad, 0x2c, 0x03, 0x8e, 0xac, 0xd7, 0x98, 0x29, 0x13, 0xc6, 0x02, 0x29, 0xb5, 0xd4, 0xe7, 0xcf, 0xcc, 0x8b, 0x83, 0xec, 0x35, 0xc7, 0x9c, 0x74, 0xb7, 0xad, 0x85, 0x5f}}},
@@ -1868,407 +1729,279 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x69, 0x3e, 0x47, 0x97, 0x2c, 0xaf, 0x52, 0x7c, 0x78, 0x83, 0xad, 0x1b, 0x39, 0x82, 0x2f, 0x02, 0x6f, 0x47, 0xdb, 0x2a, 0xb0, 0xe1, 0x91, 0x99, 0x55, 0xb8, 0x99, 0x3a, 0xa0, 0x44, 0x11, 0x51}}}
};
-static inline void p1p1_to_p2(ge25519_p2 *r, const ge25519_p1p1 *p)
+void p1p1_to_p2(ge25519_p2 *r, const ge25519_p1p1 *p)
{
- fe25519_mul(&r->x, &p->x, &p->t);
- fe25519_mul(&r->y, &p->y, &p->z);
- fe25519_mul(&r->z, &p->z, &p->t);
+ fe25519_mul(&r->x, &p->x, &p->t);
+ fe25519_mul(&r->y, &p->y, &p->z);
+ fe25519_mul(&r->z, &p->z, &p->t);
}
-static inline void p1p1_to_p2_2(ge25519_p3 *r, const ge25519_p1p1 *p)
+void p1p1_to_p2_2(ge25519_p3 *r, const ge25519_p1p1 *p)
{
- fe25519_mul(&r->x, &p->x, &p->t);
- fe25519_mul(&r->y, &p->y, &p->z);
- fe25519_mul(&r->z, &p->z, &p->t);
+ fe25519_mul(&r->x, &p->x, &p->t);
+ fe25519_mul(&r->y, &p->y, &p->z);
+ fe25519_mul(&r->z, &p->z, &p->t);
}
-static inline void p1p1_to_p3(ge25519_p3 *r, const ge25519_p1p1 *p)
+void p1p1_to_p3(ge25519_p3 *r, const ge25519_p1p1 *p)
{
- p1p1_to_p2_2(r, p);
- fe25519_mul(&r->t, &p->x, &p->y);
+ p1p1_to_p2_2(r, p);
+ fe25519_mul(&r->t, &p->x, &p->y);
}
-static void ge25519_mixadd2(ge25519_p3 *r, const ge25519_aff *q)
+void ge25519_mixadd2(ge25519_p3 *r, const ge25519_aff *q)
{
- fe25519 a,b,t1,t2,c,d,e,f,g,h,qt;
- fe25519_mul(&qt, &q->x, &q->y);
- fe25519_sub(&a, &r->y, &r->x); /* A = (Y1-X1)*(Y2-X2) */
- fe25519_add(&b, &r->y, &r->x); /* B = (Y1+X1)*(Y2+X2) */
- fe25519_sub(&t1, &q->y, &q->x);
- fe25519_add(&t2, &q->y, &q->x);
- fe25519_mul(&a, &a, &t1);
- fe25519_mul(&b, &b, &t2);
- fe25519_sub(&e, &b, &a); /* E = B-A */
- fe25519_add(&h, &b, &a); /* H = B+A */
- fe25519_mul(&c, &r->t, &qt); /* C = T1*k*T2 */
- fe25519_mul(&c, &c, &ge25519_ec2d);
- fe25519_add(&d, &r->z, &r->z); /* D = Z1*2 */
- fe25519_sub(&f, &d, &c); /* F = D-C */
- fe25519_add(&g, &d, &c); /* G = D+C */
- fe25519_mul(&r->x, &e, &f);
- fe25519_mul(&r->y, &h, &g);
- fe25519_mul(&r->z, &g, &f);
- fe25519_mul(&r->t, &e, &h);
+ fe25519 a,b,t1,t2,c,d,e,f,g,h,qt;
+ fe25519_mul(&qt, &q->x, &q->y);
+ fe25519_sub(&a, &r->y, &r->x); /* A = (Y1-X1)*(Y2-X2) */
+ fe25519_add(&b, &r->y, &r->x); /* B = (Y1+X1)*(Y2+X2) */
+ fe25519_sub(&t1, &q->y, &q->x);
+ fe25519_add(&t2, &q->y, &q->x);
+ fe25519_mul(&a, &a, &t1);
+ fe25519_mul(&b, &b, &t2);
+ fe25519_sub(&e, &b, &a); /* E = B-A */
+ fe25519_add(&h, &b, &a); /* H = B+A */
+ fe25519_mul(&c, &r->t, &qt); /* C = T1*k*T2 */
+ fe25519_mul(&c, &c, &ge25519_ec2d);
+ fe25519_add(&d, &r->z, &r->z); /* D = Z1*2 */
+ fe25519_sub(&f, &d, &c); /* F = D-C */
+ fe25519_add(&g, &d, &c); /* G = D+C */
+ fe25519_mul(&r->x, &e, &f);
+ fe25519_mul(&r->y, &h, &g);
+ fe25519_mul(&r->z, &g, &f);
+ fe25519_mul(&r->t, &e, &h);
}
-static void add_p1p1(ge25519_p1p1 *r, const ge25519_p3 *p, const ge25519_p3 *q)
+void add_p1p1(ge25519_p1p1 *r, const ge25519_p3 *p, const ge25519_p3 *q)
{
- fe25519 a, b, c, d, t;
-
- fe25519_sub(&a, &p->y, &p->x); /* A = (Y1-X1)*(Y2-X2) */
- fe25519_sub(&t, &q->y, &q->x);
- fe25519_mul(&a, &a, &t);
- fe25519_add(&b, &p->x, &p->y); /* B = (Y1+X1)*(Y2+X2) */
- fe25519_add(&t, &q->x, &q->y);
- fe25519_mul(&b, &b, &t);
- fe25519_mul(&c, &p->t, &q->t); /* C = T1*k*T2 */
- fe25519_mul(&c, &c, &ge25519_ec2d);
- fe25519_mul(&d, &p->z, &q->z); /* D = Z1*2*Z2 */
- fe25519_add(&d, &d, &d);
- fe25519_sub(&r->x, &b, &a); /* E = B-A */
- fe25519_sub(&r->t, &d, &c); /* F = D-C */
- fe25519_add(&r->z, &d, &c); /* G = D+C */
- fe25519_add(&r->y, &b, &a); /* H = B+A */
+ fe25519 a, b, c, d, t;
+
+ fe25519_sub(&a, &p->y, &p->x); /* A = (Y1-X1)*(Y2-X2) */
+ fe25519_sub(&t, &q->y, &q->x);
+ fe25519_mul(&a, &a, &t);
+ fe25519_add(&b, &p->x, &p->y); /* B = (Y1+X1)*(Y2+X2) */
+ fe25519_add(&t, &q->x, &q->y);
+ fe25519_mul(&b, &b, &t);
+ fe25519_mul(&c, &p->t, &q->t); /* C = T1*k*T2 */
+ fe25519_mul(&c, &c, &ge25519_ec2d);
+ fe25519_mul(&d, &p->z, &q->z); /* D = Z1*2*Z2 */
+ fe25519_add(&d, &d, &d);
+ fe25519_sub(&r->x, &b, &a); /* E = B-A */
+ fe25519_sub(&r->t, &d, &c); /* F = D-C */
+ fe25519_add(&r->z, &d, &c); /* G = D+C */
+ fe25519_add(&r->y, &b, &a); /* H = B+A */
}
/* See http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-dbl-2008-hwcd */
-static void dbl_p1p1(ge25519_p1p1 *r, const ge25519_p2 *p)
+void dbl_p1p1(ge25519_p1p1 *r, const ge25519_p2 *p)
{
- fe25519 a,b,c,d;
- fe25519_square(&a, &p->x);
- fe25519_square(&b, &p->y);
- fe25519_square(&c, &p->z);
- fe25519_add(&c, &c, &c);
- fe25519_neg(&d, &a);
+ fe25519 a,b,c,d;
+ fe25519_square(&a, &p->x);
+ fe25519_square(&b, &p->y);
+ fe25519_square(&c, &p->z);
+ fe25519_add(&c, &c, &c);
+ fe25519_neg(&d, &a);
- fe25519_add(&r->x, &p->x, &p->y);
- fe25519_square(&r->x, &r->x);
- fe25519_sub(&r->x, &r->x, &a);
- fe25519_sub(&r->x, &r->x, &b);
- fe25519_add(&r->z, &d, &b);
- fe25519_sub(&r->t, &r->z, &c);
- fe25519_sub(&r->y, &d, &b);
+ fe25519_add(&r->x, &p->x, &p->y);
+ fe25519_square(&r->x, &r->x);
+ fe25519_sub(&r->x, &r->x, &a);
+ fe25519_sub(&r->x, &r->x, &b);
+ fe25519_add(&r->z, &d, &b);
+ fe25519_sub(&r->t, &r->z, &c);
+ fe25519_sub(&r->y, &d, &b);
}
/* Constant-time version of: if(b) r = p */
-static inline void cmov_aff(ge25519_aff *r, const ge25519_aff *p, unsigned char b)
+void cmov_aff(ge25519_aff *r, const ge25519_aff *p, unsigned char b)
{
- fe25519_cmov(&r->x, &p->x, b);
- fe25519_cmov(&r->y, &p->y, b);
+ fe25519_cmov(&r->x, &p->x, b);
+ fe25519_cmov(&r->y, &p->y, b);
}
-static inline unsigned char equal(signed char b,signed char c)
+unsigned char equal(signed char b,signed char c)
{
- unsigned char ub = b;
- unsigned char uc = c;
- unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */
- crypto_uint32 y = x; /* 0: yes; 1..255: no */
- y -= 1; /* 4294967295: yes; 0..254: no */
- y >>= 31; /* 1: yes; 0: no */
- return (unsigned char)y;
+ unsigned char ub = b;
+ unsigned char uc = c;
+ unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */
+ crypto_uint32 y = x; /* 0: yes; 1..255: no */
+ y -= 1; /* 4294967295: yes; 0..254: no */
+ y >>= 31; /* 1: yes; 0: no */
+ return (unsigned char)y;
}
-static inline unsigned char negative(signed char b)
+unsigned char negative(signed char b)
{
- unsigned long long x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */
- x >>= 63; /* 1: yes; 0: no */
- return (unsigned char)x;
+ unsigned long long x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */
+ x >>= 63; /* 1: yes; 0: no */
+ return (unsigned char)x;
}
-static inline void choose_t(ge25519_aff *t, unsigned long long pos, signed char b)
+void choose_t(ge25519_aff *t, unsigned long long pos, signed char b)
{
- /* constant time */
- fe25519 v;
- *t = ge25519_base_multiples_affine[5*pos+0];
- cmov_aff(t, &ge25519_base_multiples_affine[5*pos+1],equal(b,1) | equal(b,-1));
- cmov_aff(t, &ge25519_base_multiples_affine[5*pos+2],equal(b,2) | equal(b,-2));
- cmov_aff(t, &ge25519_base_multiples_affine[5*pos+3],equal(b,3) | equal(b,-3));
- cmov_aff(t, &ge25519_base_multiples_affine[5*pos+4],equal(b,-4));
- fe25519_neg(&v, &t->x);
- fe25519_cmov(&t->x, &v, negative(b));
+ /* constant time */
+ fe25519 v;
+ *t = ge25519_base_multiples_affine[5*pos+0];
+ cmov_aff(t, &ge25519_base_multiples_affine[5*pos+1],equal(b,1) | equal(b,-1));
+ cmov_aff(t, &ge25519_base_multiples_affine[5*pos+2],equal(b,2) | equal(b,-2));
+ cmov_aff(t, &ge25519_base_multiples_affine[5*pos+3],equal(b,3) | equal(b,-3));
+ cmov_aff(t, &ge25519_base_multiples_affine[5*pos+4],equal(b,-4));
+ fe25519_neg(&v, &t->x);
+ fe25519_cmov(&t->x, &v, negative(b));
}
-static inline void setneutral(ge25519 *r)
+void setneutral(ge25519 *r)
{
- fe25519_setzero(&r->x);
- fe25519_setone(&r->y);
- fe25519_setone(&r->z);
- fe25519_setzero(&r->t);
+ fe25519_setzero(&r->x);
+ fe25519_setone(&r->y);
+ fe25519_setone(&r->z);
+ fe25519_setzero(&r->t);
}
/* return 0 on success, -1 otherwise */
-static int ge25519_unpackneg_vartime(ge25519_p3 *r, const unsigned char p[32])
-{
- unsigned char par;
- fe25519 t, chk, num, den, den2, den4, den6;
- fe25519_setone(&r->z);
- par = p[31] >> 7;
- fe25519_unpack(&r->y, p);
- fe25519_square(&num, &r->y); /* x = y^2 */
- fe25519_mul(&den, &num, &ge25519_ecd); /* den = dy^2 */
- fe25519_sub(&num, &num, &r->z); /* x = y^2-1 */
- fe25519_add(&den, &r->z, &den); /* den = dy^2+1 */
-
- /* Computation of sqrt(num/den) */
- /* 1.: computation of num^((p-5)/8)*den^((7p-35)/8) = (num*den^7)^((p-5)/8) */
- fe25519_square(&den2, &den);
- fe25519_square(&den4, &den2);
- fe25519_mul(&den6, &den4, &den2);
- fe25519_mul(&t, &den6, &num);
- fe25519_mul(&t, &t, &den);
-
- fe25519_pow2523(&t, &t);
- /* 2. computation of r->x = t * num * den^3 */
- fe25519_mul(&t, &t, &num);
- fe25519_mul(&t, &t, &den);
- fe25519_mul(&t, &t, &den);
- fe25519_mul(&r->x, &t, &den);
-
- /* 3. Check whether sqrt computation gave correct result, multiply by sqrt(-1) if not: */
- fe25519_square(&chk, &r->x);
- fe25519_mul(&chk, &chk, &den);
- if (!fe25519_iseq_vartime(&chk, &num))
- fe25519_mul(&r->x, &r->x, &ge25519_sqrtm1);
-
- /* 4. Now we have one of the two square roots, except if input was not a square */
- fe25519_square(&chk, &r->x);
- fe25519_mul(&chk, &chk, &den);
- if (!fe25519_iseq_vartime(&chk, &num))
- return -1;
-
- /* 5. Choose the desired square root according to parity: */
- if(fe25519_getparity(&r->x) != (1-par))
- fe25519_neg(&r->x, &r->x);
-
- fe25519_mul(&r->t, &r->x, &r->y);
- return 0;
-}
-
-static inline void ge25519_pack(unsigned char r[32], const ge25519_p3 *p)
-{
- fe25519 tx, ty, zi;
- fe25519_invert(&zi, &p->z);
- fe25519_mul(&tx, &p->x, &zi);
- fe25519_mul(&ty, &p->y, &zi);
- fe25519_pack(r, &ty);
- r[31] ^= fe25519_getparity(&tx) << 7;
-}
-
-#if 0
-static int ge25519_isneutral_vartime(const ge25519_p3 *p)
-{
- int ret = 1;
- if(!fe25519_iszero(&p->x)) ret = 0;
- if(!fe25519_iseq_vartime(&p->y, &p->z)) ret = 0;
- return ret;
+int ge25519_unpackneg_vartime(ge25519_p3 *r, const unsigned char p[32])
+{
+ unsigned char par;
+ fe25519 t, chk, num, den, den2, den4, den6;
+ fe25519_setone(&r->z);
+ par = p[31] >> 7;
+ fe25519_unpack(&r->y, p);
+ fe25519_square(&num, &r->y); /* x = y^2 */
+ fe25519_mul(&den, &num, &ge25519_ecd); /* den = dy^2 */
+ fe25519_sub(&num, &num, &r->z); /* x = y^2-1 */
+ fe25519_add(&den, &r->z, &den); /* den = dy^2+1 */
+
+ /* Computation of sqrt(num/den) */
+ /* 1.: computation of num^((p-5)/8)*den^((7p-35)/8) = (num*den^7)^((p-5)/8) */
+ fe25519_square(&den2, &den);
+ fe25519_square(&den4, &den2);
+ fe25519_mul(&den6, &den4, &den2);
+ fe25519_mul(&t, &den6, &num);
+ fe25519_mul(&t, &t, &den);
+
+ fe25519_pow2523(&t, &t);
+ /* 2. computation of r->x = t * num * den^3 */
+ fe25519_mul(&t, &t, &num);
+ fe25519_mul(&t, &t, &den);
+ fe25519_mul(&t, &t, &den);
+ fe25519_mul(&r->x, &t, &den);
+
+ /* 3. Check whether sqrt computation gave correct result, multiply by sqrt(-1) if not: */
+ fe25519_square(&chk, &r->x);
+ fe25519_mul(&chk, &chk, &den);
+ if (!fe25519_iseq_vartime(&chk, &num))
+ fe25519_mul(&r->x, &r->x, &ge25519_sqrtm1);
+
+ /* 4. Now we have one of the two square roots, except if input was not a square */
+ fe25519_square(&chk, &r->x);
+ fe25519_mul(&chk, &chk, &den);
+ if (!fe25519_iseq_vartime(&chk, &num))
+ return -1;
+
+ /* 5. Choose the desired square root according to parity: */
+ if(fe25519_getparity(&r->x) != (1-par))
+ fe25519_neg(&r->x, &r->x);
+
+ fe25519_mul(&r->t, &r->x, &r->y);
+ return 0;
+}
+
+void ge25519_pack(unsigned char r[32], const ge25519_p3 *p)
+{
+ fe25519 tx, ty, zi;
+ fe25519_invert(&zi, &p->z);
+ fe25519_mul(&tx, &p->x, &zi);
+ fe25519_mul(&ty, &p->y, &zi);
+ fe25519_pack(r, &ty);
+ r[31] ^= fe25519_getparity(&tx) << 7;
}
-#endif
/* computes [s1]p1 + [s2]p2 */
-static void ge25519_double_scalarmult_vartime(ge25519_p3 *r, const ge25519_p3 *p1, const sc25519 *s1, const ge25519_p3 *p2, const sc25519 *s2)
-{
- ge25519_p1p1 tp1p1;
- ge25519_p3 pre[16];
- char *pre5 = (char *)(&(pre[5])); // eliminate type punning warning
- unsigned char b[127];
- int i;
-
- /* precomputation s2 s1 */
- setneutral(pre); /* 00 00 */
- pre[1] = *p1; /* 00 01 */
- dbl_p1p1(&tp1p1,(ge25519_p2 *)p1); p1p1_to_p3( &pre[2], &tp1p1); /* 00 10 */
- add_p1p1(&tp1p1,&pre[1], &pre[2]); p1p1_to_p3( &pre[3], &tp1p1); /* 00 11 */
- pre[4] = *p2; /* 01 00 */
- add_p1p1(&tp1p1,&pre[1], &pre[4]); p1p1_to_p3( &pre[5], &tp1p1); /* 01 01 */
- add_p1p1(&tp1p1,&pre[2], &pre[4]); p1p1_to_p3( &pre[6], &tp1p1); /* 01 10 */
- add_p1p1(&tp1p1,&pre[3], &pre[4]); p1p1_to_p3( &pre[7], &tp1p1); /* 01 11 */
- dbl_p1p1(&tp1p1,(ge25519_p2 *)p2); p1p1_to_p3( &pre[8], &tp1p1); /* 10 00 */
- add_p1p1(&tp1p1,&pre[1], &pre[8]); p1p1_to_p3( &pre[9], &tp1p1); /* 10 01 */
- dbl_p1p1(&tp1p1,(ge25519_p2 *)pre5); p1p1_to_p3(&pre[10], &tp1p1); /* 10 10 */
- add_p1p1(&tp1p1,&pre[3], &pre[8]); p1p1_to_p3(&pre[11], &tp1p1); /* 10 11 */
- add_p1p1(&tp1p1,&pre[4], &pre[8]); p1p1_to_p3(&pre[12], &tp1p1); /* 11 00 */
- add_p1p1(&tp1p1,&pre[1],&pre[12]); p1p1_to_p3(&pre[13], &tp1p1); /* 11 01 */
- add_p1p1(&tp1p1,&pre[2],&pre[12]); p1p1_to_p3(&pre[14], &tp1p1); /* 11 10 */
- add_p1p1(&tp1p1,&pre[3],&pre[12]); p1p1_to_p3(&pre[15], &tp1p1); /* 11 11 */
-
- sc25519_2interleave2(b,s1,s2);
-
- /* scalar multiplication */
- *r = pre[b[126]];
- for(i=125;i>=0;i--)
- {
- dbl_p1p1(&tp1p1, (ge25519_p2 *)r);
- p1p1_to_p2((ge25519_p2 *) r, &tp1p1);
- dbl_p1p1(&tp1p1, (ge25519_p2 *)r);
- if(b[i]!=0)
- {
- p1p1_to_p3(r, &tp1p1);
- add_p1p1(&tp1p1, r, &pre[b[i]]);
- }
- if(i != 0) p1p1_to_p2((ge25519_p2 *)r, &tp1p1);
- else p1p1_to_p3(r, &tp1p1);
- }
-}
-
-static inline void ge25519_scalarmult_base(ge25519_p3 *r, const sc25519 *s)
-{
- signed char b[85];
- int i;
- ge25519_aff t;
- sc25519_window3(b,s);
-
- choose_t((ge25519_aff *)r, 0, b[0]);
- fe25519_setone(&r->z);
- fe25519_mul(&r->t, &r->x, &r->y);
- for(i=1;i<85;i++)
- {
- choose_t(&t, (unsigned long long) i, b[i]);
- ge25519_mixadd2(r, &t);
- }
-}
-
-static inline void get_hram(unsigned char *hram, const unsigned char *sm, const unsigned char *pk, unsigned char *playground, unsigned long long smlen)
-{
- unsigned long long i;
-
- for (i = 0;i < 32;++i) playground[i] = sm[i];
- for (i = 32;i < 64;++i) playground[i] = pk[i-32];
- for (i = 64;i < smlen;++i) playground[i] = sm[i];
-
- //crypto_hash_sha512(hram,playground,smlen);
- SHA512::hash(hram,playground,(unsigned int)smlen);
-}
-
-// This is the original sign and verify code -- the versions in sign() and
-// verify() below the fold are slightly modified in terms of how they behave
-// in relation to the message, but the algorithms are the same.
-
-#if 0
-int crypto_sign_keypair(
- unsigned char *pk,
- unsigned char *sk
- )
-{
- sc25519 scsk;
- ge25519 gepk;
- unsigned char extsk[64];
- int i;
-
- randombytes(sk, 32);
- crypto_hash_sha512(extsk, sk, 32);
- extsk[0] &= 248;
- extsk[31] &= 127;
- extsk[31] |= 64;
-
- sc25519_from32bytes(&scsk,extsk);
-
- ge25519_scalarmult_base(&gepk, &scsk);
- ge25519_pack(pk, &gepk);
- for(i=0;i<32;i++)
- sk[32 + i] = pk[i];
- return 0;
-}
-
-static int crypto_sign(
- unsigned char *sm,unsigned long long *smlen,
- const unsigned char *m,unsigned long long mlen,
- const unsigned char *sk
- )
-{
- sc25519 sck, scs, scsk;
- ge25519 ger;
- unsigned char r[32];
- unsigned char s[32];
- unsigned char extsk[64];
- unsigned long long i;
- unsigned char hmg[crypto_hash_sha512_BYTES];
- unsigned char hram[crypto_hash_sha512_BYTES];
-
- crypto_hash_sha512(extsk, sk, 32);
- extsk[0] &= 248;
- extsk[31] &= 127;
- extsk[31] |= 64;
-
- *smlen = mlen+64;
- for(i=0;i<mlen;i++)
- sm[64 + i] = m[i];
- for(i=0;i<32;i++)
- sm[32 + i] = extsk[32+i];
-
- crypto_hash_sha512(hmg, sm+32, mlen+32); /* Generate k as h(extsk[32],...,extsk[63],m) */
-
- /* Computation of R */
- sc25519_from64bytes(&sck, hmg);
- ge25519_scalarmult_base(&ger, &sck);
- ge25519_pack(r, &ger);
-
- /* Computation of s */
- for(i=0;i<32;i++)
- sm[i] = r[i];
+void ge25519_double_scalarmult_vartime(ge25519_p3 *r, const ge25519_p3 *p1, const sc25519 *s1, const ge25519_p3 *p2, const sc25519 *s2)
+{
+ ge25519_p1p1 tp1p1;
+ ge25519_p3 pre[16];
+ char *pre5 = (char *)(&(pre[5])); // eliminate type punning warning
+ unsigned char b[127];
+ int i;
+
+ /* precomputation s2 s1 */
+ setneutral(pre); /* 00 00 */
+ pre[1] = *p1; /* 00 01 */
+ dbl_p1p1(&tp1p1,(ge25519_p2 *)p1); p1p1_to_p3( &pre[2], &tp1p1); /* 00 10 */
+ add_p1p1(&tp1p1,&pre[1], &pre[2]); p1p1_to_p3( &pre[3], &tp1p1); /* 00 11 */
+ pre[4] = *p2; /* 01 00 */
+ add_p1p1(&tp1p1,&pre[1], &pre[4]); p1p1_to_p3( &pre[5], &tp1p1); /* 01 01 */
+ add_p1p1(&tp1p1,&pre[2], &pre[4]); p1p1_to_p3( &pre[6], &tp1p1); /* 01 10 */
+ add_p1p1(&tp1p1,&pre[3], &pre[4]); p1p1_to_p3( &pre[7], &tp1p1); /* 01 11 */
+ dbl_p1p1(&tp1p1,(ge25519_p2 *)p2); p1p1_to_p3( &pre[8], &tp1p1); /* 10 00 */
+ add_p1p1(&tp1p1,&pre[1], &pre[8]); p1p1_to_p3( &pre[9], &tp1p1); /* 10 01 */
+ dbl_p1p1(&tp1p1,(ge25519_p2 *)pre5); p1p1_to_p3(&pre[10], &tp1p1); /* 10 10 */
+ add_p1p1(&tp1p1,&pre[3], &pre[8]); p1p1_to_p3(&pre[11], &tp1p1); /* 10 11 */
+ add_p1p1(&tp1p1,&pre[4], &pre[8]); p1p1_to_p3(&pre[12], &tp1p1); /* 11 00 */
+ add_p1p1(&tp1p1,&pre[1],&pre[12]); p1p1_to_p3(&pre[13], &tp1p1); /* 11 01 */
+ add_p1p1(&tp1p1,&pre[2],&pre[12]); p1p1_to_p3(&pre[14], &tp1p1); /* 11 10 */
+ add_p1p1(&tp1p1,&pre[3],&pre[12]); p1p1_to_p3(&pre[15], &tp1p1); /* 11 11 */
+
+ sc25519_2interleave2(b,s1,s2);
+
+ /* scalar multiplication */
+ *r = pre[b[126]];
+ for(i=125;i>=0;i--)
+ {
+ dbl_p1p1(&tp1p1, (ge25519_p2 *)r);
+ p1p1_to_p2((ge25519_p2 *) r, &tp1p1);
+ dbl_p1p1(&tp1p1, (ge25519_p2 *)r);
+ if(b[i]!=0)
+ {
+ p1p1_to_p3(r, &tp1p1);
+ add_p1p1(&tp1p1, r, &pre[b[i]]);
+ }
+ if(i != 0) p1p1_to_p2((ge25519_p2 *)r, &tp1p1);
+ else p1p1_to_p3(r, &tp1p1);
+ }
+}
- get_hram(hram, sm, sk+32, sm, mlen+64);
+void ge25519_scalarmult_base(ge25519_p3 *r, const sc25519 *s)
+{
+ signed char b[85];
+ int i;
+ ge25519_aff t;
+ sc25519_window3(b,s);
+
+ choose_t((ge25519_aff *)r, 0, b[0]);
+ fe25519_setone(&r->z);
+ fe25519_mul(&r->t, &r->x, &r->y);
+ for(i=1;i<85;i++)
+ {
+ choose_t(&t, (unsigned long long) i, b[i]);
+ ge25519_mixadd2(r, &t);
+ }
+}
- sc25519_from64bytes(&scs, hram);
- sc25519_from32bytes(&scsk, extsk);
- sc25519_mul(&scs, &scs, &scsk);
-
- sc25519_add(&scs, &scs, &sck);
-
- sc25519_to32bytes(s,&scs); /* cat s */
- for(i=0;i<32;i++)
- sm[32 + i] = s[i];
-
- return 0;
-}
-
-static int crypto_sign_open(
- unsigned char *m,unsigned long long *mlen,
- const unsigned char *sm,unsigned long long smlen,
- const unsigned char *pk
- )
+void get_hram(unsigned char *hram, const unsigned char *sm, const unsigned char *pk, unsigned char *playground, unsigned long long smlen)
{
- int i, ret;
- unsigned char t2[32];
- ge25519 get1, get2;
- sc25519 schram, scs;
- unsigned char hram[crypto_hash_sha512_BYTES];
-
- *mlen = (unsigned long long) -1;
- if (smlen < 64) return -1;
-
- if (ge25519_unpackneg_vartime(&get1, pk)) return -1;
-
- get_hram(hram,sm,pk,m,smlen);
-
- sc25519_from64bytes(&schram, hram);
-
- sc25519_from32bytes(&scs, sm+32);
-
- ge25519_double_scalarmult_vartime(&get2, &get1, &schram, &ge25519_base, &scs);
- ge25519_pack(t2, &get2);
-
- ret = crypto_verify_32(sm, t2);
-
- if (!ret)
- {
- for(i=0;i<smlen-64;i++)
- m[i] = sm[i + 64];
- *mlen = smlen-64;
- }
- else
- {
- for(i=0;i<smlen-64;i++)
- m[i] = 0;
- }
- return ret;
-}
-#endif // 0
+ unsigned long long i;
+
+ for (i = 0;i < 32;++i) playground[i] = sm[i];
+ for (i = 32;i < 64;++i) playground[i] = pk[i-32];
+ for (i = 64;i < smlen;++i) playground[i] = sm[i];
+
+ //crypto_hash_sha512(hram,playground,smlen);
+ ZeroTier::SHA512::hash(hram,playground,(unsigned int)smlen);
+}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
+} // anonymous namespace
+
+#ifdef ZT_USE_FAST_X64_ED25519
+extern "C" void ed25519_amd64_asm_sign(const unsigned char *sk,const unsigned char *pk,const unsigned char *m,const unsigned int mlen,unsigned char *sig);
+#endif
+
+namespace ZeroTier {
+
void C25519::agree(const C25519::Private &mine,const C25519::Public &their,void *keybuf,unsigned int keylen)
- throw()
{
unsigned char rawkey[32];
unsigned char digest[64];
@@ -2285,114 +2018,113 @@ void C25519::agree(const C25519::Private &mine,const C25519::Public &their,void
}
void C25519::sign(const C25519::Private &myPrivate,const C25519::Public &myPublic,const void *msg,unsigned int len,void *signature)
- throw()
-{
- sc25519 sck, scs, scsk;
- ge25519 ger;
- unsigned char r[32];
- unsigned char s[32];
- unsigned char extsk[64];
- unsigned char hmg[crypto_hash_sha512_BYTES];
- unsigned char hram[crypto_hash_sha512_BYTES];
- unsigned char *sig = (unsigned char *)signature;
- unsigned char digest[64]; // we sign the first 32 bytes of SHA-512(msg)
-
- SHA512::hash(digest,msg,len);
-
- SHA512::hash(extsk,myPrivate.data + 32,32);
- extsk[0] &= 248;
- extsk[31] &= 127;
- extsk[31] |= 64;
-
- for(unsigned int i=0;i<32;i++)
- sig[32 + i] = extsk[32 + i];
- for(unsigned int i=0;i<32;i++)
- sig[64 + i] = digest[i];
-
- SHA512::hash(hmg,sig + 32,64);
- //crypto_hash_sha512(hmg, sm+32, mlen+32); /* Generate k as h(extsk[32],...,extsk[63],m) */
-
- /* Computation of R */
- sc25519_from64bytes(&sck, hmg);
- ge25519_scalarmult_base(&ger, &sck);
- ge25519_pack(r, &ger);
-
- /* Computation of s */
- for(unsigned int i=0;i<32;i++)
- sig[i] = r[i];
-
- get_hram(hram,sig,myPublic.data + 32,sig,96);
-
- sc25519_from64bytes(&scs, hram);
- sc25519_from32bytes(&scsk, extsk);
- sc25519_mul(&scs, &scs, &scsk);
-
- sc25519_add(&scs, &scs, &sck);
-
- sc25519_to32bytes(s,&scs); /* cat s */
- for(unsigned int i=0;i<32;i++)
- sig[32 + i] = s[i];
+{
+#ifdef ZT_USE_FAST_X64_ED25519
+ ed25519_amd64_asm_sign(myPrivate.data + 32,myPublic.data + 32,(const unsigned char *)msg,len,(unsigned char *)signature);
+#else
+ sc25519 sck, scs, scsk;
+ ge25519 ger;
+ unsigned char r[32];
+ unsigned char s[32];
+ unsigned char extsk[64];
+ unsigned char hmg[crypto_hash_sha512_BYTES];
+ unsigned char hram[crypto_hash_sha512_BYTES];
+ unsigned char *sig = (unsigned char *)signature;
+ unsigned char digest[64]; // we sign the first 32 bytes of SHA-512(msg)
+
+ SHA512::hash(digest,msg,len);
+
+ SHA512::hash(extsk,myPrivate.data + 32,32);
+ extsk[0] &= 248;
+ extsk[31] &= 127;
+ extsk[31] |= 64;
+
+ for(unsigned int i=0;i<32;i++)
+ sig[32 + i] = extsk[32 + i];
+ for(unsigned int i=0;i<32;i++)
+ sig[64 + i] = digest[i];
+
+ SHA512::hash(hmg,sig + 32,64);
+
+ /* Computation of R */
+ sc25519_from64bytes(&sck, hmg);
+ ge25519_scalarmult_base(&ger, &sck);
+ ge25519_pack(r, &ger);
+
+ /* Computation of s */
+ for(unsigned int i=0;i<32;i++)
+ sig[i] = r[i];
+
+ get_hram(hram,sig,myPublic.data + 32,sig,96);
+
+ sc25519_from64bytes(&scs, hram);
+ sc25519_from32bytes(&scsk, extsk);
+ sc25519_mul(&scs, &scs, &scsk);
+
+ sc25519_add(&scs, &scs, &sck);
+
+ sc25519_to32bytes(s,&scs); /* cat s */
+ for(unsigned int i=0;i<32;i++)
+ sig[32 + i] = s[i];
+#endif
}
bool C25519::verify(const C25519::Public &their,const void *msg,unsigned int len,const void *signature)
- throw()
{
- unsigned char t2[32];
- ge25519 get1, get2;
- sc25519 schram, scs;
- unsigned char hram[crypto_hash_sha512_BYTES];
- unsigned char m[96];
- unsigned char digest[64]; // we sign the first 32 bytes of SHA-512(msg)
- const unsigned char *sig = (const unsigned char *)signature;
+ unsigned char t2[32];
+ ge25519 get1, get2;
+ sc25519 schram, scs;
+ unsigned char hram[crypto_hash_sha512_BYTES];
+ unsigned char m[96];
+ unsigned char digest[64]; // we sign the first 32 bytes of SHA-512(msg)
+ const unsigned char *sig = (const unsigned char *)signature;
- // First check the message's integrity
- SHA512::hash(digest,msg,len);
- if (!Utils::secureEq(sig + 64,digest,32))
- return false;
+ // First check the message's integrity
+ SHA512::hash(digest,msg,len);
+ if (!Utils::secureEq(sig + 64,digest,32))
+ return false;
- if (ge25519_unpackneg_vartime(&get1,their.data + 32))
- return false;
+ if (ge25519_unpackneg_vartime(&get1,their.data + 32))
+ return false;
- get_hram(hram,sig,their.data + 32,m,96);
+ get_hram(hram,sig,their.data + 32,m,96);
- sc25519_from64bytes(&schram, hram);
+ sc25519_from64bytes(&schram, hram);
- sc25519_from32bytes(&scs, sig+32);
+ sc25519_from32bytes(&scs, sig+32);
- ge25519_double_scalarmult_vartime(&get2, &get1, &schram, &ge25519_base, &scs);
- ge25519_pack(t2, &get2);
+ ge25519_double_scalarmult_vartime(&get2, &get1, &schram, &ge25519_base, &scs);
+ ge25519_pack(t2, &get2);
- return Utils::secureEq(sig,t2,32);
+ return Utils::secureEq(sig,t2,32);
}
void C25519::_calcPubDH(C25519::Pair &kp)
- throw()
{
- // First 32 bytes of pub and priv are the keys for ECDH key
- // agreement. This generates the public portion from the private.
- crypto_scalarmult_base(kp.pub.data,kp.priv.data);
+ // First 32 bytes of pub and priv are the keys for ECDH key
+ // agreement. This generates the public portion from the private.
+ crypto_scalarmult_base(kp.pub.data,kp.priv.data);
}
void C25519::_calcPubED(C25519::Pair &kp)
- throw()
-{
- unsigned char extsk[64];
- sc25519 scsk;
- ge25519 gepk;
-
- // Second 32 bytes of pub and priv are the keys for ed25519
- // signing and verification.
- SHA512::hash(extsk,kp.priv.data + 32,32);
- extsk[0] &= 248;
- extsk[31] &= 127;
- extsk[31] |= 64;
- sc25519_from32bytes(&scsk,extsk);
- ge25519_scalarmult_base(&gepk,&scsk);
- ge25519_pack(kp.pub.data + 32,&gepk);
- // In NaCl, the public key is crammed into the next 32 bytes
- // of the private key for signing since both keys are required
- // to sign. In this version we just get it from kp.pub, so we
- // leave that out of private.
+{
+ unsigned char extsk[64];
+ sc25519 scsk;
+ ge25519 gepk;
+
+ // Second 32 bytes of pub and priv are the keys for ed25519
+ // signing and verification.
+ SHA512::hash(extsk,kp.priv.data + 32,32);
+ extsk[0] &= 248;
+ extsk[31] &= 127;
+ extsk[31] |= 64;
+ sc25519_from32bytes(&scsk,extsk);
+ ge25519_scalarmult_base(&gepk,&scsk);
+ ge25519_pack(kp.pub.data + 32,&gepk);
+ // In NaCl, the public key is crammed into the next 32 bytes
+ // of the private key for signing since both keys are required
+ // to sign. In this version we just get it from kp.pub, so we
+ // leave that out of private.
}
} // namespace ZeroTier
diff --git a/node/C25519.hpp b/node/C25519.hpp
index b19d9693..2e9184a0 100644
--- a/node/C25519.hpp
+++ b/node/C25519.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,12 +14,19 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_C25519_HPP
#define ZT_C25519_HPP
-#include "Array.hpp"
#include "Utils.hpp"
namespace ZeroTier {
@@ -34,37 +41,18 @@ namespace ZeroTier {
class C25519
{
public:
- /**
- * Public key (both crypto and signing)
- */
- typedef Array<unsigned char,ZT_C25519_PUBLIC_KEY_LEN> Public; // crypto key, signing key (both 32 bytes)
-
- /**
- * Private key (both crypto and signing)
- */
- typedef Array<unsigned char,ZT_C25519_PRIVATE_KEY_LEN> Private; // crypto key, signing key (both 32 bytes)
-
- /**
- * Message signature
- */
- typedef Array<unsigned char,ZT_C25519_SIGNATURE_LEN> Signature;
-
- /**
- * Public/private key pair
- */
- typedef struct {
- Public pub;
- Private priv;
- } Pair;
+ struct Public { uint8_t data[ZT_C25519_PUBLIC_KEY_LEN]; };
+ struct Private { uint8_t data[ZT_C25519_PRIVATE_KEY_LEN]; };
+ struct Signature { uint8_t data[ZT_C25519_SIGNATURE_LEN]; };
+ struct Pair { Public pub; Private priv; };
/**
* Generate a C25519 elliptic curve key pair
*/
static inline Pair generate()
- throw()
{
Pair kp;
- Utils::getSecureRandom(kp.priv.data,(unsigned int)kp.priv.size());
+ Utils::getSecureRandom(kp.priv.data,ZT_C25519_PRIVATE_KEY_LEN);
_calcPubDH(kp);
_calcPubED(kp);
return kp;
@@ -85,11 +73,10 @@ public:
*/
template<typename F>
static inline Pair generateSatisfying(F cond)
- throw()
{
Pair kp;
void *const priv = (void *)kp.priv.data;
- Utils::getSecureRandom(priv,(unsigned int)kp.priv.size());
+ Utils::getSecureRandom(priv,ZT_C25519_PRIVATE_KEY_LEN);
_calcPubED(kp); // do Ed25519 key -- bytes 32-63 of pub and priv
do {
++(((uint64_t *)priv)[1]);
@@ -110,13 +97,8 @@ public:
* @param keybuf Buffer to fill
* @param keylen Number of key bytes to generate
*/
- static void agree(const Private &mine,const Public &their,void *keybuf,unsigned int keylen)
- throw();
- static inline void agree(const Pair &mine,const Public &their,void *keybuf,unsigned int keylen)
- throw()
- {
- agree(mine.priv,their,keybuf,keylen);
- }
+ static void agree(const Private &mine,const Public &their,void *keybuf,unsigned int keylen);
+ static inline void agree(const Pair &mine,const Public &their,void *keybuf,unsigned int keylen) { agree(mine.priv,their,keybuf,keylen); }
/**
* Sign a message with a sender's key pair
@@ -137,13 +119,8 @@ public:
* @param len Length of message in bytes
* @param signature Buffer to fill with signature -- MUST be 96 bytes in length
*/
- static void sign(const Private &myPrivate,const Public &myPublic,const void *msg,unsigned int len,void *signature)
- throw();
- static inline void sign(const Pair &mine,const void *msg,unsigned int len,void *signature)
- throw()
- {
- sign(mine.priv,mine.pub,msg,len,signature);
- }
+ static void sign(const Private &myPrivate,const Public &myPublic,const void *msg,unsigned int len,void *signature);
+ static inline void sign(const Pair &mine,const void *msg,unsigned int len,void *signature) { sign(mine.priv,mine.pub,msg,len,signature); }
/**
* Sign a message with a sender's key pair
@@ -155,14 +132,12 @@ public:
* @return Signature
*/
static inline Signature sign(const Private &myPrivate,const Public &myPublic,const void *msg,unsigned int len)
- throw()
{
Signature sig;
sign(myPrivate,myPublic,msg,len,sig.data);
return sig;
}
static inline Signature sign(const Pair &mine,const void *msg,unsigned int len)
- throw()
{
Signature sig;
sign(mine.priv,mine.pub,msg,len,sig.data);
@@ -178,8 +153,7 @@ public:
* @param signature 96-byte signature
* @return True if signature is valid and the message is authentic and unmodified
*/
- static bool verify(const Public &their,const void *msg,unsigned int len,const void *signature)
- throw();
+ static bool verify(const Public &their,const void *msg,unsigned int len,const void *signature);
/**
* Verify a message's signature
@@ -191,7 +165,6 @@ public:
* @return True if signature is valid and the message is authentic and unmodified
*/
static inline bool verify(const Public &their,const void *msg,unsigned int len,const Signature &signature)
- throw()
{
return verify(their,msg,len,signature.data);
}
@@ -199,13 +172,11 @@ public:
private:
// derive first 32 bytes of kp.pub from first 32 bytes of kp.priv
// this is the ECDH key
- static void _calcPubDH(Pair &kp)
- throw();
+ static void _calcPubDH(Pair &kp);
// derive 2nd 32 bytes of kp.pub from 2nd 32 bytes of kp.priv
// this is the Ed25519 sign/verify key
- static void _calcPubED(Pair &kp)
- throw();
+ static void _calcPubED(Pair &kp);
};
} // namespace ZeroTier
diff --git a/node/Capability.cpp b/node/Capability.cpp
index c178e566..fb52be8a 100644
--- a/node/Capability.cpp
+++ b/node/Capability.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include "Capability.hpp"
@@ -22,6 +30,7 @@
#include "Topology.hpp"
#include "Switch.hpp"
#include "Network.hpp"
+#include "Node.hpp"
namespace ZeroTier {
@@ -51,7 +60,7 @@ int Capability::verify(const RuntimeEnvironment *RR,void *tPtr) const
if (!id.verify(tmp.data(),tmp.size(),_custody[c].signature))
return -1;
} else {
- RR->sw->requestWhois(tPtr,_custody[c].from);
+ RR->sw->requestWhois(tPtr,RR->node->now(),_custody[c].from);
return 1;
}
}
diff --git a/node/Capability.hpp b/node/Capability.hpp
index 454723ac..91a46566 100644
--- a/node/Capability.hpp
+++ b/node/Capability.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_CAPABILITY_HPP
@@ -77,7 +85,7 @@ public:
* @param rules Network flow rules for this capability
* @param ruleCount Number of flow rules
*/
- Capability(uint32_t id,uint64_t nwid,uint64_t ts,unsigned int mccl,const ZT_VirtualNetworkRule *rules,unsigned int ruleCount)
+ Capability(uint32_t id,uint64_t nwid,int64_t ts,unsigned int mccl,const ZT_VirtualNetworkRule *rules,unsigned int ruleCount)
{
memset(this,0,sizeof(Capability));
_nwid = nwid;
@@ -86,7 +94,7 @@ public:
_maxCustodyChainLength = (mccl > 0) ? ((mccl < ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH) ? mccl : (unsigned int)ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH) : 1;
_ruleCount = (ruleCount < ZT_MAX_CAPABILITY_RULES) ? ruleCount : ZT_MAX_CAPABILITY_RULES;
if (_ruleCount)
- memcpy(_rules,rules,sizeof(ZT_VirtualNetworkRule) * _ruleCount);
+ ZT_FAST_MEMCPY(_rules,rules,sizeof(ZT_VirtualNetworkRule) * _ruleCount);
}
/**
@@ -112,7 +120,7 @@ public:
/**
* @return Timestamp
*/
- inline uint64_t timestamp() const { return _ts; }
+ inline int64_t timestamp() const { return _ts; }
/**
* @return Last 'to' address in chain of custody
@@ -270,6 +278,13 @@ public:
b.append((uint32_t)rules[i].v.tag.id);
b.append((uint32_t)rules[i].v.tag.value);
break;
+ case ZT_NETWORK_RULE_MATCH_INTEGER_RANGE:
+ b.append((uint8_t)19);
+ b.append((uint64_t)rules[i].v.intRange.start);
+ b.append((uint64_t)(rules[i].v.intRange.start + (uint64_t)rules[i].v.intRange.end)); // more future-proof
+ b.append((uint16_t)rules[i].v.intRange.idx);
+ b.append((uint8_t)rules[i].v.intRange.format);
+ break;
}
}
}
@@ -305,16 +320,16 @@ public:
break;
case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
case ZT_NETWORK_RULE_MATCH_MAC_DEST:
- memcpy(rules[ruleCount].v.mac,b.field(p,6),6);
+ ZT_FAST_MEMCPY(rules[ruleCount].v.mac,b.field(p,6),6);
break;
case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
- memcpy(&(rules[ruleCount].v.ipv4.ip),b.field(p,4),4);
+ ZT_FAST_MEMCPY(&(rules[ruleCount].v.ipv4.ip),b.field(p,4),4);
rules[ruleCount].v.ipv4.mask = (uint8_t)b[p + 4];
break;
case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
- memcpy(rules[ruleCount].v.ipv6.ip,b.field(p,16),16);
+ ZT_FAST_MEMCPY(rules[ruleCount].v.ipv6.ip,b.field(p,16),16);
rules[ruleCount].v.ipv6.mask = (uint8_t)b[p + 16];
break;
case ZT_NETWORK_RULE_MATCH_IP_TOS:
@@ -358,6 +373,12 @@ public:
rules[ruleCount].v.tag.id = b.template at<uint32_t>(p);
rules[ruleCount].v.tag.value = b.template at<uint32_t>(p + 4);
break;
+ case ZT_NETWORK_RULE_MATCH_INTEGER_RANGE:
+ rules[ruleCount].v.intRange.start = b.template at<uint64_t>(p);
+ rules[ruleCount].v.intRange.end = (uint32_t)(b.template at<uint64_t>(p + 8) - rules[ruleCount].v.intRange.start);
+ rules[ruleCount].v.intRange.idx = b.template at<uint16_t>(p + 16);
+ rules[ruleCount].v.intRange.format = (uint8_t)b[p + 18];
+ break;
}
p += fieldLen;
++ruleCount;
@@ -412,26 +433,26 @@ public:
const unsigned int rc = b.template at<uint16_t>(p); p += 2;
if (rc > ZT_MAX_CAPABILITY_RULES)
- throw std::runtime_error("rule overflow");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
deserializeRules(b,p,_rules,_ruleCount,rc);
_maxCustodyChainLength = (unsigned int)b[p++];
if ((_maxCustodyChainLength < 1)||(_maxCustodyChainLength > ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH))
- throw std::runtime_error("invalid max custody chain length");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
for(unsigned int i=0;;++i) {
const Address to(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
if (!to)
break;
if ((i >= _maxCustodyChainLength)||(i >= ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH))
- throw std::runtime_error("unterminated custody chain");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
_custody[i].to = to;
_custody[i].from.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
if (b[p++] == 1) {
if (b.template at<uint16_t>(p) != ZT_C25519_SIGNATURE_LEN)
- throw std::runtime_error("invalid signature");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
p += 2;
- memcpy(_custody[i].signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
+ ZT_FAST_MEMCPY(_custody[i].signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
} else {
p += 2 + b.template at<uint16_t>(p);
}
@@ -439,7 +460,7 @@ public:
p += 2 + b.template at<uint16_t>(p);
if (p > b.size())
- throw std::runtime_error("extended field overflow");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
return (p - startAt);
}
@@ -452,7 +473,7 @@ public:
private:
uint64_t _nwid;
- uint64_t _ts;
+ int64_t _ts;
uint32_t _id;
unsigned int _maxCustodyChainLength;
diff --git a/node/CertificateOfMembership.cpp b/node/CertificateOfMembership.cpp
index 9bf70216..8184ce99 100644
--- a/node/CertificateOfMembership.cpp
+++ b/node/CertificateOfMembership.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include "CertificateOfMembership.hpp"
@@ -21,6 +29,7 @@
#include "Topology.hpp"
#include "Switch.hpp"
#include "Network.hpp"
+#include "Node.hpp"
namespace ZeroTier {
@@ -49,6 +58,7 @@ void CertificateOfMembership::setQualifier(uint64_t id,uint64_t value,uint64_t m
std::string CertificateOfMembership::toString() const
{
+ char tmp[ZT_NETWORK_COM_MAX_QUALIFIERS * 32];
std::string s;
s.append("1:"); // COM_UINT64_ED25519
@@ -61,7 +71,7 @@ std::string CertificateOfMembership::toString() const
buf[ptr++] = Utils::hton(_qualifiers[i].value);
buf[ptr++] = Utils::hton(_qualifiers[i].maxDelta);
}
- s.append(Utils::hex(buf,ptr * sizeof(uint64_t)));
+ s.append(Utils::hex(buf,ptr * sizeof(uint64_t),tmp));
delete [] buf;
} catch ( ... ) {
delete [] buf;
@@ -70,11 +80,11 @@ std::string CertificateOfMembership::toString() const
s.push_back(':');
- s.append(_signedBy.toString());
+ s.append(_signedBy.toString(tmp));
if (_signedBy) {
s.push_back(':');
- s.append(Utils::hex(_signature.data,(unsigned int)_signature.size()));
+ s.append(Utils::hex(_signature.data,ZT_C25519_SIGNATURE_LEN,tmp));
}
return s;
@@ -84,7 +94,7 @@ void CertificateOfMembership::fromString(const char *s)
{
_qualifierCount = 0;
_signedBy.zero();
- memset(_signature.data,0,_signature.size());
+ memset(_signature.data,0,ZT_C25519_SIGNATURE_LEN);
if (!*s)
return;
@@ -135,7 +145,7 @@ void CertificateOfMembership::fromString(const char *s)
colonAt = 0;
while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt;
if (colonAt) {
- if (Utils::unhex(s,colonAt,_signature.data,(unsigned int)_signature.size()) != _signature.size())
+ if (Utils::unhex(s,colonAt,_signature.data,ZT_C25519_SIGNATURE_LEN) != ZT_C25519_SIGNATURE_LEN)
_signedBy.zero();
} else {
_signedBy.zero();
@@ -214,7 +224,7 @@ int CertificateOfMembership::verify(const RuntimeEnvironment *RR,void *tPtr) con
const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
if (!id) {
- RR->sw->requestWhois(tPtr,_signedBy);
+ RR->sw->requestWhois(tPtr,RR->node->now(),_signedBy);
return 1;
}
diff --git a/node/CertificateOfMembership.hpp b/node/CertificateOfMembership.hpp
index dfccb138..b5a90007 100644
--- a/node/CertificateOfMembership.hpp
+++ b/node/CertificateOfMembership.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_CERTIFICATEOFMEMBERSHIP_HPP
@@ -111,7 +119,7 @@ public:
CertificateOfMembership(const CertificateOfMembership &c)
{
- memcpy(this,&c,sizeof(CertificateOfMembership));
+ ZT_FAST_MEMCPY(this,&c,sizeof(CertificateOfMembership));
}
/**
@@ -134,12 +142,12 @@ public:
_qualifiers[2].value = issuedTo.toInt();
_qualifiers[2].maxDelta = 0xffffffffffffffffULL;
_qualifierCount = 3;
- memset(_signature.data,0,_signature.size());
+ memset(_signature.data,0,ZT_C25519_SIGNATURE_LEN);
}
inline CertificateOfMembership &operator=(const CertificateOfMembership &c)
{
- memcpy(this,&c,sizeof(CertificateOfMembership));
+ ZT_FAST_MEMCPY(this,&c,sizeof(CertificateOfMembership));
return *this;
}
@@ -168,7 +176,7 @@ public:
/**
* @return Timestamp for this cert and maximum delta for timestamp
*/
- inline uint64_t timestamp() const
+ inline int64_t timestamp() const
{
for(unsigned int i=0;i<_qualifierCount;++i) {
if (_qualifiers[i].id == COM_RESERVED_ID_TIMESTAMP)
@@ -285,7 +293,7 @@ public:
}
_signedBy.appendTo(b);
if (_signedBy)
- b.append(_signature.data,(unsigned int)_signature.size());
+ b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
}
template<unsigned int C>
@@ -297,14 +305,14 @@ public:
_signedBy.zero();
if (b[p++] != 1)
- throw std::invalid_argument("invalid object");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
unsigned int numq = b.template at<uint16_t>(p); p += sizeof(uint16_t);
uint64_t lastId = 0;
for(unsigned int i=0;i<numq;++i) {
const uint64_t qid = b.template at<uint64_t>(p);
if (qid < lastId)
- throw std::invalid_argument("qualifiers not sorted");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_BAD_ENCODING;
else lastId = qid;
if (_qualifierCount < ZT_NETWORK_COM_MAX_QUALIFIERS) {
_qualifiers[_qualifierCount].id = qid;
@@ -313,7 +321,7 @@ public:
p += 24;
++_qualifierCount;
} else {
- throw std::invalid_argument("too many qualifiers");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
}
}
@@ -321,8 +329,8 @@ public:
p += ZT_ADDRESS_LENGTH;
if (_signedBy) {
- memcpy(_signature.data,b.field(p,(unsigned int)_signature.size()),_signature.size());
- p += (unsigned int)_signature.size();
+ ZT_FAST_MEMCPY(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
+ p += ZT_C25519_SIGNATURE_LEN;
}
return (p - startAt);
@@ -340,7 +348,7 @@ public:
if ((a.id != b.id)||(a.value != b.value)||(a.maxDelta != b.maxDelta))
return false;
}
- return (_signature == c._signature);
+ return (memcmp(_signature.data,c._signature.data,ZT_C25519_SIGNATURE_LEN) == 0);
}
inline bool operator!=(const CertificateOfMembership &c) const { return (!(*this == c)); }
@@ -351,7 +359,7 @@ private:
uint64_t id;
uint64_t value;
uint64_t maxDelta;
- inline bool operator<(const _Qualifier &q) const throw() { return (id < q.id); } // sort order
+ inline bool operator<(const _Qualifier &q) const { return (id < q.id); } // sort order
};
Address _signedBy;
diff --git a/node/CertificateOfOwnership.cpp b/node/CertificateOfOwnership.cpp
index 2bd181e0..8ee67865 100644
--- a/node/CertificateOfOwnership.cpp
+++ b/node/CertificateOfOwnership.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include "CertificateOfOwnership.hpp"
@@ -22,6 +30,7 @@
#include "Topology.hpp"
#include "Switch.hpp"
#include "Network.hpp"
+#include "Node.hpp"
namespace ZeroTier {
@@ -31,7 +40,7 @@ int CertificateOfOwnership::verify(const RuntimeEnvironment *RR,void *tPtr) cons
return -1;
const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
if (!id) {
- RR->sw->requestWhois(tPtr,_signedBy);
+ RR->sw->requestWhois(tPtr,RR->node->now(),_signedBy);
return 1;
}
try {
diff --git a/node/CertificateOfOwnership.hpp b/node/CertificateOfOwnership.hpp
index 93be64dd..278ae863 100644
--- a/node/CertificateOfOwnership.hpp
+++ b/node/CertificateOfOwnership.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_CERTIFICATEOFOWNERSHIP_HPP
@@ -64,7 +72,7 @@ public:
memset(this,0,sizeof(CertificateOfOwnership));
}
- CertificateOfOwnership(const uint64_t nwid,const uint64_t ts,const Address &issuedTo,const uint32_t id) :
+ CertificateOfOwnership(const uint64_t nwid,const int64_t ts,const Address &issuedTo,const uint32_t id) :
_networkId(nwid),
_ts(ts),
_flags(0),
@@ -72,10 +80,12 @@ public:
_thingCount(0),
_issuedTo(issuedTo)
{
+ memset(_thingTypes,0,sizeof(_thingTypes));
+ memset(_thingValues,0,sizeof(_thingValues));
}
inline uint64_t networkId() const { return _networkId; }
- inline uint64_t timestamp() const { return _ts; }
+ inline int64_t timestamp() const { return _ts; }
inline uint32_t id() const { return _id; }
inline unsigned int thingCount() const { return (unsigned int)_thingCount; }
@@ -105,11 +115,11 @@ public:
if (_thingCount >= ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) return;
if (ip.ss_family == AF_INET) {
_thingTypes[_thingCount] = THING_IPV4_ADDRESS;
- memcpy(_thingValues[_thingCount],&(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr),4);
+ ZT_FAST_MEMCPY(_thingValues[_thingCount],&(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr),4);
++_thingCount;
} else if (ip.ss_family == AF_INET6) {
_thingTypes[_thingCount] = THING_IPV6_ADDRESS;
- memcpy(_thingValues[_thingCount],reinterpret_cast<const struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
+ ZT_FAST_MEMCPY(_thingValues[_thingCount],reinterpret_cast<const struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
++_thingCount;
}
}
@@ -188,7 +198,7 @@ public:
for(unsigned int i=0,j=_thingCount;i<j;++i) {
if (i < ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) {
_thingTypes[i] = (uint8_t)b[p++];
- memcpy(_thingValues[i],b.field(p,ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE),ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE);
+ ZT_FAST_MEMCPY(_thingValues[i],b.field(p,ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE),ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE);
p += ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE;
}
}
@@ -197,16 +207,16 @@ public:
_signedBy.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
if (b[p++] == 1) {
if (b.template at<uint16_t>(p) != ZT_C25519_SIGNATURE_LEN)
- throw std::runtime_error("invalid signature length");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
p += 2;
- memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
+ ZT_FAST_MEMCPY(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
} else {
p += 2 + b.template at<uint16_t>(p);
}
p += 2 + b.template at<uint16_t>(p);
if (p > b.size())
- throw std::runtime_error("extended field overflow");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
return (p - startAt);
}
@@ -221,7 +231,7 @@ private:
bool _owns(const Thing &t,const void *v,unsigned int l) const;
uint64_t _networkId;
- uint64_t _ts;
+ int64_t _ts;
uint64_t _flags;
uint32_t _id;
uint16_t _thingCount;
diff --git a/node/CertificateOfRepresentation.hpp b/node/CertificateOfRepresentation.hpp
deleted file mode 100644
index 710ee577..00000000
--- a/node/CertificateOfRepresentation.hpp
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef ZT_CERTIFICATEOFREPRESENTATION_HPP
-#define ZT_CERTIFICATEOFREPRESENTATION_HPP
-
-#include "Constants.hpp"
-#include "Credential.hpp"
-#include "Address.hpp"
-#include "C25519.hpp"
-#include "Identity.hpp"
-#include "Buffer.hpp"
-
-/**
- * Maximum number of addresses allowed in a COR
- */
-#define ZT_CERTIFICATEOFREPRESENTATION_MAX_ADDRESSES ZT_MAX_UPSTREAMS
-
-namespace ZeroTier {
-
-/**
- * A signed enumeration of a node's roots (planet and moons)
- *
- * This is sent as part of HELLO and attests to which roots a node trusts
- * to represent it on the network. Federated roots (moons) can send these
- * further upstream to tell global roots which nodes they represent, making
- * them reachable via federated roots if they are not reachable directly.
- *
- * As of 1.2.0 this is sent but not used. Right now nodes still always
- * announce to planetary roots no matter what. In the future this can be
- * used to implement even better fault tolerance for federation for the
- * no roots are reachable case as well as a "privacy mode" where federated
- * roots can shield nodes entirely and p2p connectivity behind them can
- * be disabled. This will be desirable for a number of use cases.
- */
-class CertificateOfRepresentation : public Credential
-{
-public:
- static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_COR; }
-
- CertificateOfRepresentation()
- {
- memset(this,0,sizeof(CertificateOfRepresentation));
- }
-
- inline uint32_t id() const { return 0; }
- inline uint64_t timestamp() const { return _timestamp; }
- inline const Address &representative(const unsigned int i) const { return _reps[i]; }
- inline unsigned int repCount() const { return _repCount; }
-
- inline void clear()
- {
- memset(this,0,sizeof(CertificateOfRepresentation));
- }
-
- /**
- * Add a representative if space remains
- *
- * @param r Representative to add
- * @return True if representative was added
- */
- inline bool addRepresentative(const Address &r)
- {
- if (_repCount < ZT_CERTIFICATEOFREPRESENTATION_MAX_ADDRESSES) {
- _reps[_repCount++] = r;
- return true;
- }
- return false;
- }
-
- /**
- * Sign this COR with my identity
- *
- * @param myIdentity This node's identity
- * @param ts COR timestamp for establishing new vs. old
- */
- inline void sign(const Identity &myIdentity,const uint64_t ts)
- {
- _timestamp = ts;
- Buffer<sizeof(CertificateOfRepresentation) + 32> tmp;
- this->serialize(tmp,true);
- _signature = myIdentity.sign(tmp.data(),tmp.size());
- }
-
- /**
- * Verify this COR's signature
- *
- * @param senderIdentity Identity of sender of COR
- * @return True if COR is valid
- */
- inline bool verify(const Identity &senderIdentity)
- {
- try {
- Buffer<sizeof(CertificateOfRepresentation) + 32> tmp;
- this->serialize(tmp,true);
- return senderIdentity.verify(tmp.data(),tmp.size(),_signature.data,ZT_C25519_SIGNATURE_LEN);
- } catch ( ... ) {
- return false;
- }
- }
-
- template<unsigned int C>
- inline void serialize(Buffer<C> &b,const bool forSign = false) const
- {
- if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
-
- b.append((uint64_t)_timestamp);
- b.append((uint16_t)_repCount);
- for(unsigned int i=0;i<_repCount;++i)
- _reps[i].appendTo(b);
-
- if (!forSign) {
- b.append((uint8_t)1); // 1 == Ed25519 signature
- b.append((uint16_t)ZT_C25519_SIGNATURE_LEN);
- b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
- }
-
- b.append((uint16_t)0); // size of any additional fields, currently 0
-
- if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
- }
-
- template<unsigned int C>
- inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
- {
- clear();
-
- unsigned int p = startAt;
-
- _timestamp = b.template at<uint64_t>(p); p += 8;
- const unsigned int rc = b.template at<uint16_t>(p); p += 2;
- for(unsigned int i=0;i<rc;++i) {
- if (i < ZT_CERTIFICATEOFREPRESENTATION_MAX_ADDRESSES)
- _reps[i].setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
- p += ZT_ADDRESS_LENGTH;
- }
- _repCount = (rc > ZT_CERTIFICATEOFREPRESENTATION_MAX_ADDRESSES) ? ZT_CERTIFICATEOFREPRESENTATION_MAX_ADDRESSES : rc;
-
- if (b[p++] == 1) {
- if (b.template at<uint16_t>(p) == ZT_C25519_SIGNATURE_LEN) {
- p += 2;
- memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
- p += ZT_C25519_SIGNATURE_LEN;
- } else throw std::runtime_error("invalid signature");
- } else {
- p += 2 + b.template at<uint16_t>(p);
- }
-
- p += 2 + b.template at<uint16_t>(p);
- if (p > b.size())
- throw std::runtime_error("extended field overflow");
-
- return (p - startAt);
- }
-
-private:
- uint64_t _timestamp;
- Address _reps[ZT_CERTIFICATEOFREPRESENTATION_MAX_ADDRESSES];
- unsigned int _repCount;
- C25519::Signature _signature;
-};
-
-} // namespace ZeroTier
-
-#endif
diff --git a/node/Constants.hpp b/node/Constants.hpp
index 93184efa..23bebafd 100644
--- a/node/Constants.hpp
+++ b/node/Constants.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_CONSTANTS_HPP
@@ -52,6 +60,8 @@
#endif
#ifdef __APPLE__
+#define likely(x) __builtin_expect((x),1)
+#define unlikely(x) __builtin_expect((x),0)
#include <TargetConditionals.h>
#ifndef __UNIX_LIKE__
#define __UNIX_LIKE__
@@ -124,6 +134,28 @@
#include <endian.h>
#endif
+#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__)
+#ifndef likely
+#define likely(x) __builtin_expect((x),1)
+#endif
+#ifndef unlikely
+#define unlikely(x) __builtin_expect((x),0)
+#endif
+#else
+#ifndef likely
+#define likely(x) (x)
+#endif
+#ifndef unlikely
+#define unlikely(x) (x)
+#endif
+#endif
+
+#ifdef __WINDOWS__
+#define ZT_PACKED_STRUCT(D) __pragma(pack(push,1)) D __pragma(pack(pop))
+#else
+#define ZT_PACKED_STRUCT(D) D __attribute__((packed))
+#endif
+
/**
* Length of a ZeroTier address in bytes
*/
@@ -140,26 +172,14 @@
#define ZT_ADDRESS_RESERVED_PREFIX 0xff
/**
- * Default payload MTU for UDP packets
- *
- * In the future we might support UDP path MTU discovery, but for now we
- * set a maximum that is equal to 1500 minus 8 (for PPPoE overhead, common
- * in some markets) minus 48 (IPv6 UDP overhead).
- */
-#define ZT_UDP_DEFAULT_PAYLOAD_MTU 1444
-
-/**
* Default MTU used for Ethernet tap device
*/
-#define ZT_IF_MTU ZT_MAX_MTU
+#define ZT_DEFAULT_MTU 2800
/**
- * Maximum number of packet fragments we'll support
- *
- * The actual spec allows 16, but this is the most we'll support right
- * now. Packets with more than this many fragments are dropped.
+ * Maximum number of packet fragments we'll support (protocol max: 16)
*/
-#define ZT_MAX_PACKET_FRAGMENTS 4
+#define ZT_MAX_PACKET_FRAGMENTS 7
/**
* Size of RX queue
@@ -170,11 +190,6 @@
#define ZT_RX_QUEUE_SIZE 64
/**
- * RX queue entries older than this do not "exist"
- */
-#define ZT_RX_QUEUE_EXPIRE 4000
-
-/**
* Length of secret key in bytes -- 256-bit -- do not change
*/
#define ZT_PEER_SECRET_KEY_LENGTH 32
@@ -187,37 +202,27 @@
/**
* How often Topology::clean() and Network::clean() and similar are called, in ms
*/
-#define ZT_HOUSEKEEPING_PERIOD 120000
-
-/**
- * How long to remember peer records in RAM if they haven't been used
- */
-#define ZT_PEER_IN_MEMORY_EXPIRATION 600000
+#define ZT_HOUSEKEEPING_PERIOD 60000
/**
* Delay between WHOIS retries in ms
*/
-#define ZT_WHOIS_RETRY_DELAY 1000
-
-/**
- * Maximum identity WHOIS retries (each attempt tries consulting a different peer)
- */
-#define ZT_MAX_WHOIS_RETRIES 4
+#define ZT_WHOIS_RETRY_DELAY 500
/**
* Transmit queue entry timeout
*/
-#define ZT_TRANSMIT_QUEUE_TIMEOUT (ZT_WHOIS_RETRY_DELAY * (ZT_MAX_WHOIS_RETRIES + 1))
+#define ZT_TRANSMIT_QUEUE_TIMEOUT 5000
/**
* Receive queue entry timeout
*/
-#define ZT_RECEIVE_QUEUE_TIMEOUT (ZT_WHOIS_RETRY_DELAY * (ZT_MAX_WHOIS_RETRIES + 1))
+#define ZT_RECEIVE_QUEUE_TIMEOUT 5000
/**
* Maximum latency to allow for OK(HELLO) before packet is discarded
*/
-#define ZT_HELLO_MAX_ALLOWABLE_LATENCY 60000
+#define ZT_HELLO_MAX_ALLOWABLE_LATENCY 120000
/**
* Maximum number of ZT hops allowed (this is not IP hops/TTL)
@@ -227,11 +232,6 @@
#define ZT_RELAY_MAX_HOPS 3
/**
- * Maximum number of upstreams to use (far more than we should ever need)
- */
-#define ZT_MAX_UPSTREAMS 64
-
-/**
* Expire time for multicast 'likes' and indirect multicast memberships in ms
*/
#define ZT_MULTICAST_LIKE_EXPIRE 600000
@@ -269,16 +269,6 @@
#define ZT_PATH_HEARTBEAT_PERIOD 14000
/**
- * Paths are considered inactive if they have not received traffic in this long
- */
-#define ZT_PATH_ALIVE_TIMEOUT 45000
-
-/**
- * Minimum time between attempts to check dead paths to see if they can be re-awakened
- */
-#define ZT_PATH_MIN_REACTIVATE_INTERVAL 2500
-
-/**
* Do not accept HELLOs over a given path more often than this
*/
#define ZT_PATH_HELLO_RATE_LIMIT 1000
@@ -294,11 +284,6 @@
#define ZT_PEER_PATH_EXPIRATION ((ZT_PEER_PING_PERIOD * 4) + 3000)
/**
- * Send a full HELLO every this often (ms)
- */
-#define ZT_PEER_SEND_FULL_HELLO_EVERY (ZT_PEER_PING_PERIOD * 2)
-
-/**
* How often to retry expired paths that we're still remembering
*/
#define ZT_PEER_EXPIRED_PATH_TRIAL_PERIOD (ZT_PEER_PING_PERIOD * 10)
@@ -364,7 +349,7 @@
/**
* Time horizon for push direct paths cutoff
*/
-#define ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME 60000
+#define ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME 30000
/**
* Maximum number of direct path pushes within cutoff time
@@ -373,12 +358,12 @@
* per CUTOFF_TIME milliseconds per peer to prevent this from being
* useful for DOS amplification attacks.
*/
-#define ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT 5
+#define ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT 8
/**
* Maximum number of paths per IP scope (e.g. global, link-local) and family (e.g. v4/v6)
*/
-#define ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY 4
+#define ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY 8
/**
* Time horizon for VERB_NETWORK_CREDENTIALS cutoff
@@ -454,4 +439,13 @@
#define ZT_ETHERTYPE_IPX_B 0x8138
#define ZT_ETHERTYPE_IPV6 0x86dd
+#define ZT_EXCEPTION_OUT_OF_BOUNDS 100
+#define ZT_EXCEPTION_OUT_OF_MEMORY 101
+#define ZT_EXCEPTION_PRIVATE_KEY_REQUIRED 102
+#define ZT_EXCEPTION_INVALID_ARGUMENT 103
+#define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE 200
+#define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW 201
+#define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN 202
+#define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_BAD_ENCODING 203
+
#endif
diff --git a/node/Credential.hpp b/node/Credential.hpp
index 0ae2a0a8..34e94162 100644
--- a/node/Credential.hpp
+++ b/node/Credential.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_CREDENTIAL_HPP
@@ -48,7 +56,6 @@ public:
CREDENTIAL_TYPE_CAPABILITY = 2,
CREDENTIAL_TYPE_TAG = 3,
CREDENTIAL_TYPE_COO = 4, // CertificateOfOwnership
- CREDENTIAL_TYPE_COR = 5, // CertificateOfRepresentation
CREDENTIAL_TYPE_REVOCATION = 6
};
};
diff --git a/node/Dictionary.hpp b/node/Dictionary.hpp
index 0db13b63..f89b6ffc 100644
--- a/node/Dictionary.hpp
+++ b/node/Dictionary.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_DICTIONARY_HPP
@@ -54,43 +62,29 @@ template<unsigned int C>
class Dictionary
{
public:
- Dictionary()
- {
- _d[0] = (char)0;
- }
-
- Dictionary(const char *s)
- {
- if (s) {
- Utils::scopy(_d,sizeof(_d),s);
- } else {
- _d[0] = (char)0;
- }
- }
-
+ Dictionary() { memset(_d,0,sizeof(_d)); }
+ Dictionary(const char *s) { this->load(s); }
Dictionary(const char *s,unsigned int len)
{
- if (s) {
- if (len > (C-1))
- len = C-1;
- memcpy(_d,s,len);
- _d[len] = (char)0;
- } else {
- _d[0] = (char)0;
+ for(unsigned int i=0;i<C;++i) {
+ if ((s)&&(i < len)) {
+ if (!(_d[i] = *s))
+ s = (const char *)0;
+ else ++s;
+ } else _d[i] = (char)0;
}
+ _d[C - 1] = (char)0;
}
-
- Dictionary(const Dictionary &d)
- {
- Utils::scopy(_d,sizeof(_d),d._d);
- }
+ Dictionary(const Dictionary &d) { memcpy(_d,d._d,C); }
inline Dictionary &operator=(const Dictionary &d)
{
- Utils::scopy(_d,sizeof(_d),d._d);
+ memcpy(_d,d._d,C);
return *this;
}
+ inline operator bool() const { return (_d[0] != 0); }
+
/**
* Load a dictionary from a C-string
*
@@ -99,12 +93,15 @@ public:
*/
inline bool load(const char *s)
{
- if (s) {
- return Utils::scopy(_d,sizeof(_d),s);
- } else {
- _d[0] = (char)0;
- return true;
+ for(unsigned int i=0;i<C;++i) {
+ if (s) {
+ if (!(_d[i] = *s))
+ s = (const char *)0;
+ else ++s;
+ } else _d[i] = (char)0;
}
+ _d[C - 1] = (char)0;
+ return (!s);
}
/**
@@ -112,7 +109,7 @@ public:
*/
inline void clear()
{
- _d[0] = (char)0;
+ memset(_d,0,sizeof(_d));
}
/**
@@ -279,6 +276,21 @@ public:
}
/**
+ * Get an unsigned int64 stored as hex in the dictionary
+ *
+ * @param key Key to look up
+ * @param dfl Default value or 0 if unspecified
+ * @return Decoded hex UInt value or 'dfl' if not found
+ */
+ inline int64_t getI(const char *key,int64_t dfl = 0) const
+ {
+ char tmp[128];
+ if (this->get(key,tmp,sizeof(tmp)) >= 1)
+ return Utils::hexStrTo64(tmp);
+ return dfl;
+ }
+
+ /**
* Add a new key=value pair
*
* If the key is already present this will append another, but the first
@@ -381,8 +393,21 @@ public:
inline bool add(const char *key,uint64_t value)
{
char tmp[32];
- Utils::snprintf(tmp,sizeof(tmp),"%llx",(unsigned long long)value);
- return this->add(key,tmp,-1);
+ return this->add(key,Utils::hex(value,tmp),-1);
+ }
+
+ /**
+ * Add a 64-bit integer (unsigned) as a hex value
+ */
+ inline bool add(const char *key,int64_t value)
+ {
+ char tmp[32];
+ if (value >= 0) {
+ return this->add(key,Utils::hex((uint64_t)value,tmp),-1);
+ } else {
+ tmp[0] = '-';
+ return this->add(key,Utils::hex((uint64_t)(value * -1),tmp+1),-1);
+ }
}
/**
@@ -391,8 +416,7 @@ public:
inline bool add(const char *key,const Address &a)
{
char tmp[32];
- Utils::snprintf(tmp,sizeof(tmp),"%.10llx",(unsigned long long)a.toInt());
- return this->add(key,tmp,-1);
+ return this->add(key,Utils::hex(a.toInt(),tmp),-1);
}
/**
diff --git a/node/Hashtable.hpp b/node/Hashtable.hpp
index 66f2990a..777e88dc 100644
--- a/node/Hashtable.hpp
+++ b/node/Hashtable.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,11 +14,21 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_HASHTABLE_HPP
#define ZT_HASHTABLE_HPP
+#include "Constants.hpp"
+
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -32,11 +42,6 @@ namespace ZeroTier {
/**
* A minimal hash table implementation for the ZeroTier core
- *
- * This is not a drop-in replacement for STL containers, and has several
- * limitations. Keys can be uint64_t or an object, and if the latter they
- * must implement a method called hashCode() that returns an unsigned long
- * value that is evenly distributed.
*/
template<typename K,typename V>
class Hashtable
@@ -100,7 +105,7 @@ public:
Hashtable *_ht;
_Bucket *_b;
};
- friend class Hashtable::Iterator;
+ //friend class Hashtable<K,V>::Iterator;
/**
* @param bc Initial capacity in buckets (default: 64, must be nonzero)
@@ -111,7 +116,7 @@ public:
_s(0)
{
if (!_t)
- throw std::bad_alloc();
+ throw ZT_EXCEPTION_OUT_OF_MEMORY;
for(unsigned long i=0;i<bc;++i)
_t[i] = (_Bucket *)0;
}
@@ -122,7 +127,7 @@ public:
_s(ht._s)
{
if (!_t)
- throw std::bad_alloc();
+ throw ZT_EXCEPTION_OUT_OF_MEMORY;
for(unsigned long i=0;i<_bc;++i)
_t[i] = (_Bucket *)0;
for(unsigned long i=0;i<_bc;++i) {
@@ -251,6 +256,24 @@ public:
inline const V *get(const K &k) const { return const_cast<Hashtable *>(this)->get(k); }
/**
+ * @param k Key
+ * @param v Value to fill with result
+ * @return True if value was found and set (if false, v is not modified)
+ */
+ inline bool get(const K &k,V &v) const
+ {
+ _Bucket *b = _t[_hc(k) % _bc];
+ while (b) {
+ if (b->k == k) {
+ v = b->v;
+ return true;
+ }
+ b = b->next;
+ }
+ return false;
+ }
+
+ /**
* @param k Key to check
* @return True if key is present
*/
@@ -351,12 +374,12 @@ public:
/**
* @return Number of entries
*/
- inline unsigned long size() const throw() { return _s; }
+ inline unsigned long size() const { return _s; }
/**
* @return True if table is empty
*/
- inline bool empty() const throw() { return (_s == 0); }
+ inline bool empty() const { return (_s == 0); }
private:
template<typename O>
@@ -366,12 +389,7 @@ private:
}
static inline unsigned long _hc(const uint64_t i)
{
- /* NOTE: this assumes that 'i' is evenly distributed, which is the case for
- * packet IDs and network IDs -- the two use cases in ZT for uint64_t keys.
- * These values are also greater than 0xffffffff so they'll map onto a full
- * bucket count just fine no matter what happens. Normally you'd want to
- * hash an integer key index in a hash table. */
- return (unsigned long)i;
+ return (unsigned long)(i ^ (i >> 32)); // good for network IDs and addresses
}
static inline unsigned long _hc(const uint32_t i)
{
diff --git a/node/Identity.cpp b/node/Identity.cpp
index d1b21e9c..03f27083 100644
--- a/node/Identity.cpp
+++ b/node/Identity.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -75,12 +83,11 @@ static inline void _computeMemoryHardHash(const void *publicKey,unsigned int pub
// threshold value.
struct _Identity_generate_cond
{
- _Identity_generate_cond() throw() {}
- _Identity_generate_cond(unsigned char *sb,char *gm) throw() : digest(sb),genmem(gm) {}
+ _Identity_generate_cond() {}
+ _Identity_generate_cond(unsigned char *sb,char *gm) : digest(sb),genmem(gm) {}
inline bool operator()(const C25519::Pair &kp) const
- throw()
{
- _computeMemoryHardHash(kp.pub.data,(unsigned int)kp.pub.size(),digest,genmem);
+ _computeMemoryHardHash(kp.pub.data,ZT_C25519_PUBLIC_KEY_LEN,digest,genmem);
return (digest[0] < ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN);
}
unsigned char *digest;
@@ -113,7 +120,7 @@ bool Identity::locallyValidate() const
unsigned char digest[64];
char *genmem = new char[ZT_IDENTITY_GEN_MEMORY];
- _computeMemoryHardHash(_publicKey.data,(unsigned int)_publicKey.size(),digest,genmem);
+ _computeMemoryHardHash(_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN,digest,genmem);
delete [] genmem;
unsigned char addrb[5];
@@ -128,61 +135,79 @@ bool Identity::locallyValidate() const
(digest[63] == addrb[4]));
}
-std::string Identity::toString(bool includePrivate) const
+char *Identity::toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const
{
- std::string r;
-
- r.append(_address.toString());
- r.append(":0:"); // 0 == ZT_OBJECT_TYPE_IDENTITY
- r.append(Utils::hex(_publicKey.data,(unsigned int)_publicKey.size()));
+ char *p = buf;
+ Utils::hex10(_address.toInt(),p);
+ p += 10;
+ *(p++) = ':';
+ *(p++) = '0';
+ *(p++) = ':';
+ Utils::hex(_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN,p);
+ p += ZT_C25519_PUBLIC_KEY_LEN * 2;
if ((_privateKey)&&(includePrivate)) {
- r.push_back(':');
- r.append(Utils::hex(_privateKey->data,(unsigned int)_privateKey->size()));
+ *(p++) = ':';
+ Utils::hex(_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN,p);
+ p += ZT_C25519_PRIVATE_KEY_LEN * 2;
}
-
- return r;
+ *p = (char)0;
+ return buf;
}
bool Identity::fromString(const char *str)
{
- if (!str)
+ if (!str) {
+ _address.zero();
return false;
-
- char *saveptr = (char *)0;
- char tmp[1024];
- if (!Utils::scopy(tmp,sizeof(tmp),str))
+ }
+ char tmp[ZT_IDENTITY_STRING_BUFFER_LENGTH];
+ if (!Utils::scopy(tmp,sizeof(tmp),str)) {
+ _address.zero();
return false;
+ }
delete _privateKey;
_privateKey = (C25519::Private *)0;
int fno = 0;
+ char *saveptr = (char *)0;
for(char *f=Utils::stok(tmp,":",&saveptr);(f);f=Utils::stok((char *)0,":",&saveptr)) {
switch(fno++) {
case 0:
_address = Address(Utils::hexStrToU64(f));
- if (_address.isReserved())
+ if (_address.isReserved()) {
+ _address.zero();
return false;
+ }
break;
case 1:
- if ((f[0] != '0')||(f[1]))
+ if ((f[0] != '0')||(f[1])) {
+ _address.zero();
return false;
+ }
break;
case 2:
- if (Utils::unhex(f,_publicKey.data,(unsigned int)_publicKey.size()) != _publicKey.size())
+ if (Utils::unhex(f,_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN) != ZT_C25519_PUBLIC_KEY_LEN) {
+ _address.zero();
return false;
+ }
break;
case 3:
_privateKey = new C25519::Private();
- if (Utils::unhex(f,_privateKey->data,(unsigned int)_privateKey->size()) != _privateKey->size())
+ if (Utils::unhex(f,_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN) != ZT_C25519_PRIVATE_KEY_LEN) {
+ _address.zero();
return false;
+ }
break;
default:
+ _address.zero();
return false;
}
}
- if (fno < 3)
+ if (fno < 3) {
+ _address.zero();
return false;
+ }
return true;
}
diff --git a/node/Identity.hpp b/node/Identity.hpp
index e4522732..74716aa0 100644
--- a/node/Identity.hpp
+++ b/node/Identity.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_IDENTITY_HPP
@@ -21,16 +29,16 @@
#include <stdio.h>
#include <stdlib.h>
-#include <string>
#include "Constants.hpp"
-#include "Array.hpp"
#include "Utils.hpp"
#include "Address.hpp"
#include "C25519.hpp"
#include "Buffer.hpp"
#include "SHA512.hpp"
+#define ZT_IDENTITY_STRING_BUFFER_LENGTH 384
+
namespace ZeroTier {
/**
@@ -58,20 +66,11 @@ public:
{
}
- Identity(const char *str)
- throw(std::invalid_argument) :
- _privateKey((C25519::Private *)0)
- {
- if (!fromString(str))
- throw std::invalid_argument(std::string("invalid string-serialized identity: ") + str);
- }
-
- Identity(const std::string &str)
- throw(std::invalid_argument) :
+ Identity(const char *str) :
_privateKey((C25519::Private *)0)
{
if (!fromString(str))
- throw std::invalid_argument(std::string("invalid string-serialized identity: ") + str);
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
}
template<unsigned int C>
@@ -83,7 +82,10 @@ public:
~Identity()
{
- delete _privateKey;
+ if (_privateKey) {
+ Utils::burn(_privateKey,sizeof(C25519::Private));
+ delete _privateKey;
+ }
}
inline Identity &operator=(const Identity &id)
@@ -118,7 +120,7 @@ public:
/**
* @return True if this identity contains a private key
*/
- inline bool hasPrivate() const throw() { return (_privateKey != (C25519::Private *)0); }
+ inline bool hasPrivate() const { return (_privateKey != (C25519::Private *)0); }
/**
* Compute the SHA512 hash of our private key (if we have one)
@@ -142,11 +144,10 @@ public:
* @param len Length of data
*/
inline C25519::Signature sign(const void *data,unsigned int len) const
- throw(std::runtime_error)
{
if (_privateKey)
return C25519::sign(*_privateKey,_publicKey,data,len);
- throw std::runtime_error("sign() requires a private key");
+ throw ZT_EXCEPTION_PRIVATE_KEY_REQUIRED;
}
/**
@@ -200,7 +201,7 @@ public:
/**
* @return This identity's address
*/
- inline const Address &address() const throw() { return _address; }
+ inline const Address &address() const { return _address; }
/**
* Serialize this identity (binary)
@@ -214,10 +215,10 @@ public:
{
_address.appendTo(b);
b.append((uint8_t)0); // C25519/Ed25519 identity type
- b.append(_publicKey.data,(unsigned int)_publicKey.size());
+ b.append(_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN);
if ((_privateKey)&&(includePrivate)) {
- b.append((unsigned char)_privateKey->size());
- b.append(_privateKey->data,(unsigned int)_privateKey->size());
+ b.append((unsigned char)ZT_C25519_PRIVATE_KEY_LEN);
+ b.append(_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN);
} else b.append((unsigned char)0);
}
@@ -245,17 +246,17 @@ public:
p += ZT_ADDRESS_LENGTH;
if (b[p++] != 0)
- throw std::invalid_argument("unsupported identity type");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
- memcpy(_publicKey.data,b.field(p,(unsigned int)_publicKey.size()),(unsigned int)_publicKey.size());
- p += (unsigned int)_publicKey.size();
+ ZT_FAST_MEMCPY(_publicKey.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN);
+ p += ZT_C25519_PUBLIC_KEY_LEN;
unsigned int privateKeyLength = (unsigned int)b[p++];
if (privateKeyLength) {
if (privateKeyLength != ZT_C25519_PRIVATE_KEY_LEN)
- throw std::invalid_argument("invalid private key");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
_privateKey = new C25519::Private();
- memcpy(_privateKey->data,b.field(p,ZT_C25519_PRIVATE_KEY_LEN),ZT_C25519_PRIVATE_KEY_LEN);
+ ZT_FAST_MEMCPY(_privateKey->data,b.field(p,ZT_C25519_PRIVATE_KEY_LEN),ZT_C25519_PRIVATE_KEY_LEN);
p += ZT_C25519_PRIVATE_KEY_LEN;
}
@@ -266,9 +267,10 @@ public:
* Serialize to a more human-friendly string
*
* @param includePrivate If true, include private key (if it exists)
+ * @param buf Buffer to store string
* @return ASCII string representation of identity
*/
- std::string toString(bool includePrivate) const;
+ char *toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const;
/**
* Deserialize a human-friendly string
@@ -280,7 +282,6 @@ public:
* @return True if deserialization appears successful
*/
bool fromString(const char *str);
- inline bool fromString(const std::string &str) { return fromString(str.c_str()); }
/**
* @return C25519 public key
@@ -303,14 +304,14 @@ public:
/**
* @return True if this identity contains something
*/
- inline operator bool() const throw() { return (_address); }
-
- inline bool operator==(const Identity &id) const throw() { return ((_address == id._address)&&(_publicKey == id._publicKey)); }
- inline bool operator<(const Identity &id) const throw() { return ((_address < id._address)||((_address == id._address)&&(_publicKey < id._publicKey))); }
- inline bool operator!=(const Identity &id) const throw() { return !(*this == id); }
- inline bool operator>(const Identity &id) const throw() { return (id < *this); }
- inline bool operator<=(const Identity &id) const throw() { return !(id < *this); }
- inline bool operator>=(const Identity &id) const throw() { return !(*this < id); }
+ inline operator bool() const { return (_address); }
+
+ inline bool operator==(const Identity &id) const { return ((_address == id._address)&&(memcmp(_publicKey.data,id._publicKey.data,ZT_C25519_PUBLIC_KEY_LEN) == 0)); }
+ inline bool operator<(const Identity &id) const { return ((_address < id._address)||((_address == id._address)&&(memcmp(_publicKey.data,id._publicKey.data,ZT_C25519_PUBLIC_KEY_LEN) < 0))); }
+ inline bool operator!=(const Identity &id) const { return !(*this == id); }
+ inline bool operator>(const Identity &id) const { return (id < *this); }
+ inline bool operator<=(const Identity &id) const { return !(id < *this); }
+ inline bool operator>=(const Identity &id) const { return !(*this < id); }
private:
Address _address;
diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp
index 303160ec..ff4fc94b 100644
--- a/node/IncomingPacket.cpp
+++ b/node/IncomingPacket.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -34,13 +42,12 @@
#include "Salsa20.hpp"
#include "SHA512.hpp"
#include "World.hpp"
-#include "Cluster.hpp"
#include "Node.hpp"
#include "CertificateOfMembership.hpp"
-#include "CertificateOfRepresentation.hpp"
#include "Capability.hpp"
#include "Tag.hpp"
#include "Revocation.hpp"
+#include "Trace.hpp"
namespace ZeroTier {
@@ -56,11 +63,11 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr)
// If this is marked as a packet via a trusted path, check source address and path ID.
// Obviously if no trusted paths are configured this always returns false and such
// packets are dropped on the floor.
- if (RR->topology->shouldInboundPathBeTrusted(_path->address(),trustedPathId())) {
+ const uint64_t tpid = trustedPathId();
+ if (RR->topology->shouldInboundPathBeTrusted(_path->address(),tpid)) {
trusted = true;
- TRACE("TRUSTED PATH packet approved from %s(%s), trusted path ID %llx",sourceAddress.toString().c_str(),_path->address().toString().c_str(),trustedPathId());
} else {
- TRACE("dropped packet from %s(%s), cipher set to trusted path mode but path %llx@%s is not trusted!",sourceAddress.toString().c_str(),_path->address().toString().c_str(),trustedPathId(),_path->address().toString().c_str());
+ RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,packetId(),sourceAddress,hops(),"path not trusted");
return true;
}
} else if ((c == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)&&(verb() == Packet::VERB_HELLO)) {
@@ -72,26 +79,22 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr)
if (peer) {
if (!trusted) {
if (!dearmor(peer->key())) {
- //fprintf(stderr,"dropped packet from %s(%s), MAC authentication failed (size: %u)" ZT_EOL_S,sourceAddress.toString().c_str(),_path->address().toString().c_str(),size());
- TRACE("dropped packet from %s(%s), MAC authentication failed (size: %u)",sourceAddress.toString().c_str(),_path->address().toString().c_str(),size());
+ RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,packetId(),sourceAddress,hops(),"invalid MAC");
return true;
}
}
if (!uncompress()) {
- //fprintf(stderr,"dropped packet from %s(%s), compressed data invalid (size %u, verb may be %u)" ZT_EOL_S,sourceAddress.toString().c_str(),_path->address().toString().c_str(),size(),(unsigned int)verb());
- TRACE("dropped packet from %s(%s), compressed data invalid (size %u, verb may be %u)",sourceAddress.toString().c_str(),_path->address().toString().c_str(),size(),(unsigned int)verb());
+ RR->t->incomingPacketInvalid(tPtr,_path,packetId(),sourceAddress,hops(),Packet::VERB_NOP,"LZ4 decompression failed");
return true;
}
const Packet::Verb v = verb();
- //TRACE("<< %s from %s(%s)",Packet::verbString(v),sourceAddress.toString().c_str(),_path->address().toString().c_str());
switch(v) {
//case Packet::VERB_NOP:
default: // ignore unknown verbs, but if they pass auth check they are "received"
- peer->received(tPtr,_path,hops(),packetId(),v,0,Packet::VERB_NOP,false);
+ peer->received(tPtr,_path,hops(),packetId(),v,0,Packet::VERB_NOP,false,0);
return true;
-
case Packet::VERB_HELLO: return _doHELLO(RR,tPtr,true);
case Packet::VERB_ERROR: return _doERROR(RR,tPtr,peer);
case Packet::VERB_OK: return _doOK(RR,tPtr,peer);
@@ -107,1361 +110,1071 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr)
case Packet::VERB_MULTICAST_GATHER: return _doMULTICAST_GATHER(RR,tPtr,peer);
case Packet::VERB_MULTICAST_FRAME: return _doMULTICAST_FRAME(RR,tPtr,peer);
case Packet::VERB_PUSH_DIRECT_PATHS: return _doPUSH_DIRECT_PATHS(RR,tPtr,peer);
- case Packet::VERB_CIRCUIT_TEST: return _doCIRCUIT_TEST(RR,tPtr,peer);
- case Packet::VERB_CIRCUIT_TEST_REPORT: return _doCIRCUIT_TEST_REPORT(RR,tPtr,peer);
case Packet::VERB_USER_MESSAGE: return _doUSER_MESSAGE(RR,tPtr,peer);
+ case Packet::VERB_REMOTE_TRACE: return _doREMOTE_TRACE(RR,tPtr,peer);
}
} else {
- RR->sw->requestWhois(tPtr,sourceAddress);
+ RR->sw->requestWhois(tPtr,RR->node->now(),sourceAddress);
return false;
}
} catch ( ... ) {
- // Exceptions are more informatively caught in _do...() handlers but
- // this outer try/catch will catch anything else odd.
- TRACE("dropped ??? from %s(%s): unexpected exception in tryDecode()",sourceAddress.toString().c_str(),_path->address().toString().c_str());
+ RR->t->incomingPacketInvalid(tPtr,_path,packetId(),sourceAddress,hops(),verb(),"unexpected exception in tryDecode()");
return true;
}
}
bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- const Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_ERROR_IDX_IN_RE_VERB];
- const uint64_t inRePacketId = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_IN_RE_PACKET_ID);
- const Packet::ErrorCode errorCode = (Packet::ErrorCode)(*this)[ZT_PROTO_VERB_ERROR_IDX_ERROR_CODE];
-
- //TRACE("ERROR %s from %s(%s) in-re %s",Packet::errorString(errorCode),peer->address().toString().c_str(),_path->address().toString().c_str(),Packet::verbString(inReVerb));
-
- /* Security note: we do not gate doERROR() with expectingReplyTo() to
- * avoid having to log every outgoing packet ID. Instead we put the
- * logic to determine whether we should consider an ERROR in each
- * error handler. In most cases these are only trusted in specific
- * circumstances. */
-
- switch(errorCode) {
-
- case Packet::ERROR_OBJ_NOT_FOUND:
- // Object not found, currently only meaningful from network controllers.
- if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
- const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
- if ((network)&&(network->controller() == peer->address()))
- network->setNotFound();
- }
- break;
-
- case Packet::ERROR_UNSUPPORTED_OPERATION:
- // This can be sent in response to any operation, though right now we only
- // consider it meaningful from network controllers. This would indicate
- // that the queried node does not support acting as a controller.
- if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
- const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
- if ((network)&&(network->controller() == peer->address()))
- network->setNotFound();
- }
- break;
-
- case Packet::ERROR_IDENTITY_COLLISION:
- // FIXME: for federation this will need a payload with a signature or something.
- if (RR->topology->isUpstream(peer->identity()))
- RR->node->postEvent(tPtr,ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION);
- break;
-
- case Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE: {
- // Peers can send this in response to frames if they do not have a recent enough COM from us
+ const Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_ERROR_IDX_IN_RE_VERB];
+ const uint64_t inRePacketId = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_IN_RE_PACKET_ID);
+ const Packet::ErrorCode errorCode = (Packet::ErrorCode)(*this)[ZT_PROTO_VERB_ERROR_IDX_ERROR_CODE];
+ uint64_t networkId = 0;
+
+ /* Security note: we do not gate doERROR() with expectingReplyTo() to
+ * avoid having to log every outgoing packet ID. Instead we put the
+ * logic to determine whether we should consider an ERROR in each
+ * error handler. In most cases these are only trusted in specific
+ * circumstances. */
+
+ switch(errorCode) {
+
+ case Packet::ERROR_OBJ_NOT_FOUND:
+ // Object not found, currently only meaningful from network controllers.
+ if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
- const uint64_t now = RR->node->now();
- if ( (network) && (network->config().com) && (peer->rateGateIncomingComRequest(now)) )
- network->pushCredentialsNow(tPtr,peer->address(),now);
- } break;
+ if ((network)&&(network->controller() == peer->address()))
+ network->setNotFound();
+ }
+ break;
- case Packet::ERROR_NETWORK_ACCESS_DENIED_: {
- // Network controller: network access denied.
+ case Packet::ERROR_UNSUPPORTED_OPERATION:
+ // This can be sent in response to any operation, though right now we only
+ // consider it meaningful from network controllers. This would indicate
+ // that the queried node does not support acting as a controller.
+ if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
if ((network)&&(network->controller() == peer->address()))
- network->setAccessDenied();
- } break;
+ network->setNotFound();
+ }
+ break;
+
+ case Packet::ERROR_IDENTITY_COLLISION:
+ // FIXME: for federation this will need a payload with a signature or something.
+ if (RR->topology->isUpstream(peer->identity()))
+ RR->node->postEvent(tPtr,ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION);
+ break;
+
+ case Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE: {
+ // Peers can send this in response to frames if they do not have a recent enough COM from us
+ networkId = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD);
+ const SharedPtr<Network> network(RR->node->network(networkId));
+ const int64_t now = RR->node->now();
+ if ( (network) && (network->config().com) && (peer->rateGateIncomingComRequest(now)) )
+ network->pushCredentialsNow(tPtr,peer->address(),now);
+ } break;
+
+ case Packet::ERROR_NETWORK_ACCESS_DENIED_: {
+ // Network controller: network access denied.
+ const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
+ if ((network)&&(network->controller() == peer->address()))
+ network->setAccessDenied();
+ } break;
+
+ case Packet::ERROR_UNWANTED_MULTICAST: {
+ // Members of networks can use this error to indicate that they no longer
+ // want to receive multicasts on a given channel.
+ networkId = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD);
+ const SharedPtr<Network> network(RR->node->network(networkId));
+ if ((network)&&(network->gate(tPtr,peer))) {
+ const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8,6),6),at<uint32_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 14));
+ RR->mc->remove(network->id(),mg,peer->address());
+ }
+ } break;
- case Packet::ERROR_UNWANTED_MULTICAST: {
- // Members of networks can use this error to indicate that they no longer
- // want to receive multicasts on a given channel.
- const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
- if ((network)&&(network->gate(tPtr,peer))) {
- const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8,6),6),at<uint32_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 14));
- TRACE("%.16llx: peer %s unsubscrubed from multicast group %s",network->id(),peer->address().toString().c_str(),mg.toString().c_str());
- RR->mc->remove(network->id(),mg,peer->address());
- }
- } break;
+ default: break;
+ }
- default: break;
- }
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_ERROR,inRePacketId,inReVerb,false,networkId);
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_ERROR,inRePacketId,inReVerb,false);
- } catch ( ... ) {
- TRACE("dropped ERROR from %s(%s): unexpected exception",peer->address().toString().c_str(),_path->address().toString().c_str());
- }
return true;
}
bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool alreadyAuthenticated)
{
- try {
- const uint64_t now = RR->node->now();
-
- const uint64_t pid = packetId();
- const Address fromAddress(source());
- const unsigned int protoVersion = (*this)[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION];
- const unsigned int vMajor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION];
- const unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION];
- const unsigned int vRevision = at<uint16_t>(ZT_PROTO_VERB_HELLO_IDX_REVISION);
- const uint64_t timestamp = at<uint64_t>(ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP);
- Identity id;
- unsigned int ptr = ZT_PROTO_VERB_HELLO_IDX_IDENTITY + id.deserialize(*this,ZT_PROTO_VERB_HELLO_IDX_IDENTITY);
-
- if (protoVersion < ZT_PROTO_VERSION_MIN) {
- TRACE("dropped HELLO from %s(%s): protocol version too old",id.address().toString().c_str(),_path->address().toString().c_str());
- return true;
- }
- if (fromAddress != id.address()) {
- TRACE("dropped HELLO from %s(%s): identity does not match packet source address",fromAddress.toString().c_str(),_path->address().toString().c_str());
- return true;
- }
+ const int64_t now = RR->node->now();
+
+ const uint64_t pid = packetId();
+ const Address fromAddress(source());
+ const unsigned int protoVersion = (*this)[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION];
+ const unsigned int vMajor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION];
+ const unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION];
+ const unsigned int vRevision = at<uint16_t>(ZT_PROTO_VERB_HELLO_IDX_REVISION);
+ const int64_t timestamp = at<int64_t>(ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP);
+ Identity id;
+ unsigned int ptr = ZT_PROTO_VERB_HELLO_IDX_IDENTITY + id.deserialize(*this,ZT_PROTO_VERB_HELLO_IDX_IDENTITY);
+
+ if (protoVersion < ZT_PROTO_VERSION_MIN) {
+ RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"protocol version too old");
+ return true;
+ }
+ if (fromAddress != id.address()) {
+ RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"identity/address mismatch");
+ return true;
+ }
- SharedPtr<Peer> peer(RR->topology->getPeer(tPtr,id.address()));
- if (peer) {
- // We already have an identity with this address -- check for collisions
- if (!alreadyAuthenticated) {
- if (peer->identity() != id) {
- // Identity is different from the one we already have -- address collision
-
- // Check rate limits
- if (!RR->node->rateGateIdentityVerification(now,_path->address()))
- return true;
-
- uint8_t key[ZT_PEER_SECRET_KEY_LENGTH];
- if (RR->identity.agree(id,key,ZT_PEER_SECRET_KEY_LENGTH)) {
- if (dearmor(key)) { // ensure packet is authentic, otherwise drop
- TRACE("rejected HELLO from %s(%s): address already claimed",id.address().toString().c_str(),_path->address().toString().c_str());
- Packet outp(id.address(),RR->identity.address(),Packet::VERB_ERROR);
- outp.append((uint8_t)Packet::VERB_HELLO);
- outp.append((uint64_t)pid);
- outp.append((uint8_t)Packet::ERROR_IDENTITY_COLLISION);
- outp.armor(key,true,_path->nextOutgoingCounter());
- _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
- } else {
- TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_path->address().toString().c_str());
- }
- } else {
- TRACE("rejected HELLO from %s(%s): key agreement failed",id.address().toString().c_str(),_path->address().toString().c_str());
- }
+ SharedPtr<Peer> peer(RR->topology->getPeer(tPtr,id.address()));
+ if (peer) {
+ // We already have an identity with this address -- check for collisions
+ if (!alreadyAuthenticated) {
+ if (peer->identity() != id) {
+ // Identity is different from the one we already have -- address collision
+ // Check rate limits
+ if (!RR->node->rateGateIdentityVerification(now,_path->address()))
return true;
- } else {
- // Identity is the same as the one we already have -- check packet integrity
- if (!dearmor(peer->key())) {
- TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_path->address().toString().c_str());
- return true;
+ uint8_t key[ZT_PEER_SECRET_KEY_LENGTH];
+ if (RR->identity.agree(id,key,ZT_PEER_SECRET_KEY_LENGTH)) {
+ if (dearmor(key)) { // ensure packet is authentic, otherwise drop
+ RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"address collision");
+ Packet outp(id.address(),RR->identity.address(),Packet::VERB_ERROR);
+ outp.append((uint8_t)Packet::VERB_HELLO);
+ outp.append((uint64_t)pid);
+ outp.append((uint8_t)Packet::ERROR_IDENTITY_COLLISION);
+ outp.armor(key,true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
+ } else {
+ RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid MAC");
}
-
- // Continue at // VALID
+ } else {
+ RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid identity");
}
- } // else if alreadyAuthenticated then continue at // VALID
- } else {
- // We don't already have an identity with this address -- validate and learn it
-
- // Sanity check: this basically can't happen
- if (alreadyAuthenticated) {
- TRACE("dropped HELLO from %s(%s): somehow already authenticated with unknown peer?",id.address().toString().c_str(),_path->address().toString().c_str());
- return true;
- }
- // Check rate limits
- if (!RR->node->rateGateIdentityVerification(now,_path->address()))
return true;
+ } else {
+ // Identity is the same as the one we already have -- check packet integrity
- // Check packet integrity and MAC (this is faster than locallyValidate() so do it first to filter out total crap)
- SharedPtr<Peer> newPeer(new Peer(RR,RR->identity,id));
- if (!dearmor(newPeer->key())) {
- TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_path->address().toString().c_str());
- return true;
- }
+ if (!dearmor(peer->key())) {
+ RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid MAC");
+ return true;
+ }
- // Check that identity's address is valid as per the derivation function
- if (!id.locallyValidate()) {
- TRACE("dropped HELLO from %s(%s): identity invalid",id.address().toString().c_str(),_path->address().toString().c_str());
- return true;
+ // Continue at // VALID
}
+ } // else if alreadyAuthenticated then continue at // VALID
+ } else {
+ // We don't already have an identity with this address -- validate and learn it
- peer = RR->topology->addPeer(tPtr,newPeer);
-
- // Continue at // VALID
+ // Sanity check: this basically can't happen
+ if (alreadyAuthenticated) {
+ RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"illegal alreadyAuthenticated state");
+ return true;
}
- // VALID -- if we made it here, packet passed identity and authenticity checks!
+ // Check rate limits
+ if (!RR->node->rateGateIdentityVerification(now,_path->address())) {
+ RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"rate limit exceeded");
+ return true;
+ }
- // Get external surface address if present (was not in old versions)
- InetAddress externalSurfaceAddress;
- if (ptr < size()) {
- ptr += externalSurfaceAddress.deserialize(*this,ptr);
- if ((externalSurfaceAddress)&&(hops() == 0))
- RR->sa->iam(tPtr,id.address(),_path->localAddress(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(id),now);
+ // Check packet integrity and MAC (this is faster than locallyValidate() so do it first to filter out total crap)
+ SharedPtr<Peer> newPeer(new Peer(RR,RR->identity,id));
+ if (!dearmor(newPeer->key())) {
+ RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid MAC");
+ return true;
}
- // Get primary planet world ID and world timestamp if present
- uint64_t planetWorldId = 0;
- uint64_t planetWorldTimestamp = 0;
- if ((ptr + 16) <= size()) {
- planetWorldId = at<uint64_t>(ptr); ptr += 8;
- planetWorldTimestamp = at<uint64_t>(ptr); ptr += 8;
+ // Check that identity's address is valid as per the derivation function
+ if (!id.locallyValidate()) {
+ RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"invalid identity");
+ return true;
}
- std::vector< std::pair<uint64_t,uint64_t> > moonIdsAndTimestamps;
- if (ptr < size()) {
- // Remainder of packet, if present, is encrypted
- cryptField(peer->key(),ptr,size() - ptr);
+ peer = RR->topology->addPeer(tPtr,newPeer);
- // Get moon IDs and timestamps if present
- if ((ptr + 2) <= size()) {
- const unsigned int numMoons = at<uint16_t>(ptr); ptr += 2;
- for(unsigned int i=0;i<numMoons;++i) {
- if ((World::Type)(*this)[ptr++] == World::TYPE_MOON)
- moonIdsAndTimestamps.push_back(std::pair<uint64_t,uint64_t>(at<uint64_t>(ptr),at<uint64_t>(ptr + 8)));
- ptr += 16;
- }
- }
+ // Continue at // VALID
+ }
- // Handle COR if present (older versions don't send this)
- if ((ptr + 2) <= size()) {
- if (at<uint16_t>(ptr) > 0) {
- CertificateOfRepresentation cor;
- ptr += 2;
- ptr += cor.deserialize(*this,ptr);
- } else ptr += 2;
- }
- }
+ // VALID -- if we made it here, packet passed identity and authenticity checks!
- // Send OK(HELLO) with an echo of the packet's timestamp and some of the same
- // information about us: version, sent-to address, etc.
+ // Get external surface address if present (was not in old versions)
+ InetAddress externalSurfaceAddress;
+ if (ptr < size()) {
+ ptr += externalSurfaceAddress.deserialize(*this,ptr);
+ if ((externalSurfaceAddress)&&(hops() == 0))
+ RR->sa->iam(tPtr,id.address(),_path->localSocket(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(id),now);
+ }
- Packet outp(id.address(),RR->identity.address(),Packet::VERB_OK);
- outp.append((unsigned char)Packet::VERB_HELLO);
- outp.append((uint64_t)pid);
- outp.append((uint64_t)timestamp);
- outp.append((unsigned char)ZT_PROTO_VERSION);
- outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
- outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
- outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
+ // Get primary planet world ID and world timestamp if present
+ uint64_t planetWorldId = 0;
+ uint64_t planetWorldTimestamp = 0;
+ if ((ptr + 16) <= size()) {
+ planetWorldId = at<uint64_t>(ptr); ptr += 8;
+ planetWorldTimestamp = at<uint64_t>(ptr); ptr += 8;
+ }
- if (protoVersion >= 5) {
- _path->address().serialize(outp);
- } else {
- /* LEGACY COMPATIBILITY HACK:
- *
- * For a while now (since 1.0.3), ZeroTier has recognized changes in
- * its network environment empirically by examining its external network
- * address as reported by trusted peers. In versions prior to 1.1.0
- * (protocol version < 5), they did this by saving a snapshot of this
- * information (in SelfAwareness.hpp) keyed by reporting device ID and
- * address type.
- *
- * This causes problems when clustering is combined with symmetric NAT.
- * Symmetric NAT remaps ports, so different endpoints in a cluster will
- * report back different exterior addresses. Since the old code keys
- * this by device ID and not sending physical address and compares the
- * entire address including port, it constantly thinks its external
- * surface is changing and resets connections when talking to a cluster.
- *
- * In new code we key by sending physical address and device and we also
- * take the more conservative position of only interpreting changes in
- * IP address (neglecting port) as a change in network topology that
- * necessitates a reset. But we can make older clients work here by
- * nulling out the port field. Since this info is only used for empirical
- * detection of link changes, it doesn't break anything else.
- */
- InetAddress tmpa(_path->address());
- tmpa.setPort(0);
- tmpa.serialize(outp);
+ std::vector< std::pair<uint64_t,uint64_t> > moonIdsAndTimestamps;
+ if (ptr < size()) {
+ // Remainder of packet, if present, is encrypted
+ cryptField(peer->key(),ptr,size() - ptr);
+
+ // Get moon IDs and timestamps if present
+ if ((ptr + 2) <= size()) {
+ const unsigned int numMoons = at<uint16_t>(ptr); ptr += 2;
+ for(unsigned int i=0;i<numMoons;++i) {
+ if ((World::Type)(*this)[ptr++] == World::TYPE_MOON)
+ moonIdsAndTimestamps.push_back(std::pair<uint64_t,uint64_t>(at<uint64_t>(ptr),at<uint64_t>(ptr + 8)));
+ ptr += 16;
+ }
}
+ }
- const unsigned int worldUpdateSizeAt = outp.size();
- outp.addSize(2); // make room for 16-bit size field
- if ((planetWorldId)&&(RR->topology->planetWorldTimestamp() > planetWorldTimestamp)&&(planetWorldId == RR->topology->planetWorldId())) {
- RR->topology->planet().serialize(outp,false);
- }
- if (moonIdsAndTimestamps.size() > 0) {
- std::vector<World> moons(RR->topology->moons());
- for(std::vector<World>::const_iterator m(moons.begin());m!=moons.end();++m) {
- for(std::vector< std::pair<uint64_t,uint64_t> >::const_iterator i(moonIdsAndTimestamps.begin());i!=moonIdsAndTimestamps.end();++i) {
- if (i->first == m->id()) {
- if (m->timestamp() > i->second)
- m->serialize(outp,false);
- break;
- }
+ // Send OK(HELLO) with an echo of the packet's timestamp and some of the same
+ // information about us: version, sent-to address, etc.
+
+ Packet outp(id.address(),RR->identity.address(),Packet::VERB_OK);
+ outp.append((unsigned char)Packet::VERB_HELLO);
+ outp.append((uint64_t)pid);
+ outp.append((uint64_t)timestamp);
+ outp.append((unsigned char)ZT_PROTO_VERSION);
+ outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
+ outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
+ outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
+
+ if (protoVersion >= 5) {
+ _path->address().serialize(outp);
+ } else {
+ /* LEGACY COMPATIBILITY HACK:
+ *
+ * For a while now (since 1.0.3), ZeroTier has recognized changes in
+ * its network environment empirically by examining its external network
+ * address as reported by trusted peers. In versions prior to 1.1.0
+ * (protocol version < 5), they did this by saving a snapshot of this
+ * information (in SelfAwareness.hpp) keyed by reporting device ID and
+ * address type.
+ *
+ * This causes problems when clustering is combined with symmetric NAT.
+ * Symmetric NAT remaps ports, so different endpoints in a cluster will
+ * report back different exterior addresses. Since the old code keys
+ * this by device ID and not sending physical address and compares the
+ * entire address including port, it constantly thinks its external
+ * surface is changing and resets connections when talking to a cluster.
+ *
+ * In new code we key by sending physical address and device and we also
+ * take the more conservative position of only interpreting changes in
+ * IP address (neglecting port) as a change in network topology that
+ * necessitates a reset. But we can make older clients work here by
+ * nulling out the port field. Since this info is only used for empirical
+ * detection of link changes, it doesn't break anything else.
+ */
+ InetAddress tmpa(_path->address());
+ tmpa.setPort(0);
+ tmpa.serialize(outp);
+ }
+
+ const unsigned int worldUpdateSizeAt = outp.size();
+ outp.addSize(2); // make room for 16-bit size field
+ if ((planetWorldId)&&(RR->topology->planetWorldTimestamp() > planetWorldTimestamp)&&(planetWorldId == RR->topology->planetWorldId())) {
+ RR->topology->planet().serialize(outp,false);
+ }
+ if (moonIdsAndTimestamps.size() > 0) {
+ std::vector<World> moons(RR->topology->moons());
+ for(std::vector<World>::const_iterator m(moons.begin());m!=moons.end();++m) {
+ for(std::vector< std::pair<uint64_t,uint64_t> >::const_iterator i(moonIdsAndTimestamps.begin());i!=moonIdsAndTimestamps.end();++i) {
+ if (i->first == m->id()) {
+ if (m->timestamp() > i->second)
+ m->serialize(outp,false);
+ break;
}
}
}
- outp.setAt<uint16_t>(worldUpdateSizeAt,(uint16_t)(outp.size() - (worldUpdateSizeAt + 2)));
+ }
+ outp.setAt<uint16_t>(worldUpdateSizeAt,(uint16_t)(outp.size() - (worldUpdateSizeAt + 2)));
- const unsigned int corSizeAt = outp.size();
- outp.addSize(2);
- RR->topology->appendCertificateOfRepresentation(outp);
- outp.setAt(corSizeAt,(uint16_t)(outp.size() - (corSizeAt + 2)));
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),now);
- outp.armor(peer->key(),true,_path->nextOutgoingCounter());
- _path->send(RR,tPtr,outp.data(),outp.size(),now);
+ peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); // important for this to go first so received() knows the version
+ peer->received(tPtr,_path,hops(),pid,Packet::VERB_HELLO,0,Packet::VERB_NOP,false,0);
- peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); // important for this to go first so received() knows the version
- peer->received(tPtr,_path,hops(),pid,Packet::VERB_HELLO,0,Packet::VERB_NOP,false);
- } catch ( ... ) {
- TRACE("dropped HELLO from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
- }
return true;
}
bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- const Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_OK_IDX_IN_RE_VERB];
- const uint64_t inRePacketId = at<uint64_t>(ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID);
+ const Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_OK_IDX_IN_RE_VERB];
+ const uint64_t inRePacketId = at<uint64_t>(ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID);
+ uint64_t networkId = 0;
- if (!RR->node->expectingReplyTo(inRePacketId)) {
- TRACE("%s(%s): OK(%s) DROPPED: not expecting reply to %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),Packet::verbString(inReVerb),packetId());
- return true;
- }
+ if (!RR->node->expectingReplyTo(inRePacketId))
+ return true;
- //TRACE("%s(%s): OK(%s)",peer->address().toString().c_str(),_path->address().toString().c_str(),Packet::verbString(inReVerb));
+ switch(inReVerb) {
- switch(inReVerb) {
+ case Packet::VERB_HELLO: {
+ const uint64_t latency = RR->node->now() - at<uint64_t>(ZT_PROTO_VERB_HELLO__OK__IDX_TIMESTAMP);
+ if (latency > ZT_HELLO_MAX_ALLOWABLE_LATENCY)
+ return true;
- case Packet::VERB_HELLO: {
- const uint64_t latency = RR->node->now() - at<uint64_t>(ZT_PROTO_VERB_HELLO__OK__IDX_TIMESTAMP);
- if (latency > ZT_HELLO_MAX_ALLOWABLE_LATENCY)
- return true;
+ const unsigned int vProto = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_PROTOCOL_VERSION];
+ const unsigned int vMajor = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_MAJOR_VERSION];
+ const unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_MINOR_VERSION];
+ const unsigned int vRevision = at<uint16_t>(ZT_PROTO_VERB_HELLO__OK__IDX_REVISION);
+ if (vProto < ZT_PROTO_VERSION_MIN)
+ return true;
- const unsigned int vProto = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_PROTOCOL_VERSION];
- const unsigned int vMajor = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_MAJOR_VERSION];
- const unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_MINOR_VERSION];
- const unsigned int vRevision = at<uint16_t>(ZT_PROTO_VERB_HELLO__OK__IDX_REVISION);
+ InetAddress externalSurfaceAddress;
+ unsigned int ptr = ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2;
- if (vProto < ZT_PROTO_VERSION_MIN) {
- TRACE("%s(%s): OK(HELLO) dropped, protocol version too old",source().toString().c_str(),_path->address().toString().c_str());
- return true;
- }
+ // Get reported external surface address if present
+ if (ptr < size())
+ ptr += externalSurfaceAddress.deserialize(*this,ptr);
- InetAddress externalSurfaceAddress;
- unsigned int ptr = ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2;
-
- // Get reported external surface address if present
- if (ptr < size())
- ptr += externalSurfaceAddress.deserialize(*this,ptr);
-
- // Handle planet or moon updates if present
- if ((ptr + 2) <= size()) {
- const unsigned int worldsLen = at<uint16_t>(ptr); ptr += 2;
- if (RR->topology->shouldAcceptWorldUpdateFrom(peer->address())) {
- const unsigned int endOfWorlds = ptr + worldsLen;
- while (ptr < endOfWorlds) {
- World w;
- ptr += w.deserialize(*this,ptr);
- RR->topology->addWorld(tPtr,w,false);
- }
- } else {
- ptr += worldsLen;
+ // Handle planet or moon updates if present
+ if ((ptr + 2) <= size()) {
+ const unsigned int worldsLen = at<uint16_t>(ptr); ptr += 2;
+ if (RR->topology->shouldAcceptWorldUpdateFrom(peer->address())) {
+ const unsigned int endOfWorlds = ptr + worldsLen;
+ while (ptr < endOfWorlds) {
+ World w;
+ ptr += w.deserialize(*this,ptr);
+ RR->topology->addWorld(tPtr,w,false);
}
+ } else {
+ ptr += worldsLen;
}
+ }
- // Handle certificate of representation if present
- if ((ptr + 2) <= size()) {
- if (at<uint16_t>(ptr) > 0) {
- CertificateOfRepresentation cor;
- ptr += 2;
- ptr += cor.deserialize(*this,ptr);
- } else ptr += 2;
- }
+ if (!hops())
+ _path->updateLatency((unsigned int)latency);
-#ifdef ZT_TRACE
- const std::string tmp1(source().toString());
- const std::string tmp2(_path->address().toString());
- TRACE("%s(%s): OK(HELLO), version %u.%u.%u, latency %u",tmp1.c_str(),tmp2.c_str(),vMajor,vMinor,vRevision,latency);
-#endif
+ peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision);
- if (!hops())
- peer->addDirectLatencyMeasurment((unsigned int)latency);
- peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision);
+ if ((externalSurfaceAddress)&&(hops() == 0))
+ RR->sa->iam(tPtr,peer->address(),_path->localSocket(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(peer->identity()),RR->node->now());
+ } break;
- if ((externalSurfaceAddress)&&(hops() == 0))
- RR->sa->iam(tPtr,peer->address(),_path->localAddress(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(peer->identity()),RR->node->now());
- } break;
+ case Packet::VERB_WHOIS:
+ if (RR->topology->isUpstream(peer->identity())) {
+ const Identity id(*this,ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY);
+ RR->sw->doAnythingWaitingForPeer(tPtr,RR->topology->addPeer(tPtr,SharedPtr<Peer>(new Peer(RR,RR->identity,id))));
+ }
+ break;
+
+ case Packet::VERB_NETWORK_CONFIG_REQUEST: {
+ networkId = at<uint64_t>(ZT_PROTO_VERB_OK_IDX_PAYLOAD);
+ const SharedPtr<Network> network(RR->node->network(networkId));
+ if (network)
+ network->handleConfigChunk(tPtr,packetId(),source(),*this,ZT_PROTO_VERB_OK_IDX_PAYLOAD);
+ } break;
+
+ case Packet::VERB_MULTICAST_GATHER: {
+ networkId = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_NETWORK_ID);
+ const SharedPtr<Network> network(RR->node->network(networkId));
+ if (network) {
+ const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_ADI));
+ const unsigned int count = at<uint16_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 4);
+ RR->mc->addMultiple(tPtr,RR->node->now(),networkId,mg,field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 6,count * 5),count,at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS));
+ }
+ } break;
- case Packet::VERB_WHOIS:
- if (RR->topology->isUpstream(peer->identity())) {
- const Identity id(*this,ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY);
- RR->sw->doAnythingWaitingForPeer(tPtr,RR->topology->addPeer(tPtr,SharedPtr<Peer>(new Peer(RR,RR->identity,id))));
- }
- break;
+ case Packet::VERB_MULTICAST_FRAME: {
+ const unsigned int flags = (*this)[ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_FLAGS];
+ networkId = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_NETWORK_ID);
+ const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_ADI));
- case Packet::VERB_NETWORK_CONFIG_REQUEST: {
- const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_OK_IDX_PAYLOAD)));
- if (network)
- network->handleConfigChunk(tPtr,packetId(),source(),*this,ZT_PROTO_VERB_OK_IDX_PAYLOAD);
- } break;
+ const SharedPtr<Network> network(RR->node->network(networkId));
+ if (network) {
+ unsigned int offset = 0;
- case Packet::VERB_MULTICAST_GATHER: {
- const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_NETWORK_ID);
- const SharedPtr<Network> network(RR->node->network(nwid));
- if (network) {
- const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_ADI));
- //TRACE("%s(%s): OK(MULTICAST_GATHER) %.16llx/%s length %u",source().toString().c_str(),_path->address().toString().c_str(),nwid,mg.toString().c_str(),size());
- const unsigned int count = at<uint16_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 4);
- RR->mc->addMultiple(tPtr,RR->node->now(),nwid,mg,field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 6,count * 5),count,at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS));
+ if ((flags & 0x01) != 0) { // deprecated but still used by older peers
+ CertificateOfMembership com;
+ offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS);
+ if (com)
+ network->addCredential(tPtr,com);
}
- } break;
-
- case Packet::VERB_MULTICAST_FRAME: {
- const unsigned int flags = (*this)[ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_FLAGS];
- const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_NETWORK_ID);
- const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_ADI));
-
- //TRACE("%s(%s): OK(MULTICAST_FRAME) %.16llx/%s flags %.2x",peer->address().toString().c_str(),_path->address().toString().c_str(),nwid,mg.toString().c_str(),flags);
-
- const SharedPtr<Network> network(RR->node->network(nwid));
- if (network) {
- unsigned int offset = 0;
-
- if ((flags & 0x01) != 0) { // deprecated but still used by older peers
- CertificateOfMembership com;
- offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS);
- if (com)
- network->addCredential(tPtr,com);
- }
- if ((flags & 0x02) != 0) {
- // OK(MULTICAST_FRAME) includes implicit gather results
- offset += ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS;
- unsigned int totalKnown = at<uint32_t>(offset); offset += 4;
- unsigned int count = at<uint16_t>(offset); offset += 2;
- RR->mc->addMultiple(tPtr,RR->node->now(),nwid,mg,field(offset,count * 5),count,totalKnown);
- }
+ if ((flags & 0x02) != 0) {
+ // OK(MULTICAST_FRAME) includes implicit gather results
+ offset += ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS;
+ unsigned int totalKnown = at<uint32_t>(offset); offset += 4;
+ unsigned int count = at<uint16_t>(offset); offset += 2;
+ RR->mc->addMultiple(tPtr,RR->node->now(),networkId,mg,field(offset,count * 5),count,totalKnown);
}
- } break;
-
- default: break;
- }
+ }
+ } break;
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_OK,inRePacketId,inReVerb,false);
- } catch ( ... ) {
- TRACE("dropped OK from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
+ default: break;
}
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_OK,inRePacketId,inReVerb,false,networkId);
+
return true;
}
bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- if ((!RR->topology->amRoot())&&(!peer->rateGateInboundWhoisRequest(RR->node->now()))) {
- TRACE("dropped WHOIS from %s(%s): rate limit circuit breaker tripped",source().toString().c_str(),_path->address().toString().c_str());
- return true;
- }
-
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
- outp.append((unsigned char)Packet::VERB_WHOIS);
- outp.append(packetId());
+ if ((!RR->topology->amUpstream())&&(!peer->rateGateInboundWhoisRequest(RR->node->now())))
+ return true;
- unsigned int count = 0;
- unsigned int ptr = ZT_PACKET_IDX_PAYLOAD;
- while ((ptr + ZT_ADDRESS_LENGTH) <= size()) {
- const Address addr(field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
- ptr += ZT_ADDRESS_LENGTH;
+ Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
+ outp.append((unsigned char)Packet::VERB_WHOIS);
+ outp.append(packetId());
- const Identity id(RR->topology->getIdentity(tPtr,addr));
- if (id) {
- id.serialize(outp,false);
- ++count;
- } else {
- // Request unknown WHOIS from upstream from us (if we have one)
- RR->sw->requestWhois(tPtr,addr);
-#ifdef ZT_ENABLE_CLUSTER
- // Distribute WHOIS queries across a cluster if we do not know the ID.
- // This may result in duplicate OKs to the querying peer, which is fine.
- if (RR->cluster)
- RR->cluster->sendDistributedQuery(*this);
-#endif
- }
- }
+ unsigned int count = 0;
+ unsigned int ptr = ZT_PACKET_IDX_PAYLOAD;
+ while ((ptr + ZT_ADDRESS_LENGTH) <= size()) {
+ const Address addr(field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
+ ptr += ZT_ADDRESS_LENGTH;
- if (count > 0) {
- outp.armor(peer->key(),true,_path->nextOutgoingCounter());
- _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
+ const Identity id(RR->topology->getIdentity(tPtr,addr));
+ if (id) {
+ id.serialize(outp,false);
+ ++count;
+ } else {
+ // Request unknown WHOIS from upstream from us (if we have one)
+ RR->sw->requestWhois(tPtr,RR->node->now(),addr);
}
+ }
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_WHOIS,0,Packet::VERB_NOP,false);
- } catch ( ... ) {
- TRACE("dropped WHOIS from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
+ if (count > 0) {
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
}
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_WHOIS,0,Packet::VERB_NOP,false,0);
+
return true;
}
bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- if (!RR->topology->isUpstream(peer->identity())) {
- TRACE("RENDEZVOUS from %s ignored since source is not upstream",peer->address().toString().c_str());
- } else {
- const Address with(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
- const SharedPtr<Peer> rendezvousWith(RR->topology->getPeer(tPtr,with));
- if (rendezvousWith) {
- const unsigned int port = at<uint16_t>(ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT);
- const unsigned int addrlen = (*this)[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN];
- if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) {
- const InetAddress atAddr(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port);
- if (RR->node->shouldUsePathForZeroTierTraffic(tPtr,with,_path->localAddress(),atAddr)) {
- RR->node->putPacket(tPtr,_path->localAddress(),atAddr,"ABRE",4,2); // send low-TTL junk packet to 'open' local NAT(s) and stateful firewalls
- rendezvousWith->attemptToContactAt(tPtr,_path->localAddress(),atAddr,RR->node->now(),false,0);
- TRACE("RENDEZVOUS from %s says %s might be at %s, sent verification attempt",peer->address().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str());
- } else {
- TRACE("RENDEZVOUS from %s says %s might be at %s, ignoring since path is not suitable",peer->address().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str());
- }
- } else {
- TRACE("dropped corrupt RENDEZVOUS from %s(%s) (bad address or port)",peer->address().toString().c_str(),_path->address().toString().c_str());
+ if (RR->topology->isUpstream(peer->identity())) {
+ const Address with(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
+ const SharedPtr<Peer> rendezvousWith(RR->topology->getPeer(tPtr,with));
+ if (rendezvousWith) {
+ const unsigned int port = at<uint16_t>(ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT);
+ const unsigned int addrlen = (*this)[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN];
+ if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) {
+ const InetAddress atAddr(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port);
+ if (RR->node->shouldUsePathForZeroTierTraffic(tPtr,with,_path->localSocket(),atAddr)) {
+ const uint64_t junk = RR->node->prng();
+ RR->node->putPacket(tPtr,_path->localSocket(),atAddr,&junk,4,2); // send low-TTL junk packet to 'open' local NAT(s) and stateful firewalls
+ rendezvousWith->attemptToContactAt(tPtr,_path->localSocket(),atAddr,RR->node->now(),false);
}
- } else {
- TRACE("ignored RENDEZVOUS from %s(%s) to meet unknown peer %s",peer->address().toString().c_str(),_path->address().toString().c_str(),with.toString().c_str());
}
}
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP,false);
- } catch ( ... ) {
- TRACE("dropped RENDEZVOUS from %s(%s): unexpected exception",peer->address().toString().c_str(),_path->address().toString().c_str());
}
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP,false,0);
+
return true;
}
bool IncomingPacket::_doFRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID);
- const SharedPtr<Network> network(RR->node->network(nwid));
- bool trustEstablished = false;
- if (network) {
- if (network->gate(tPtr,peer)) {
- trustEstablished = true;
- if (size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) {
- const unsigned int etherType = at<uint16_t>(ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE);
- const MAC sourceMac(peer->address(),nwid);
- const unsigned int frameLen = size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD;
- const uint8_t *const frameData = reinterpret_cast<const uint8_t *>(data()) + ZT_PROTO_VERB_FRAME_IDX_PAYLOAD;
- if (network->filterIncomingPacket(tPtr,peer,RR->identity.address(),sourceMac,network->mac(),frameData,frameLen,etherType,0) > 0)
- RR->node->putFrame(tPtr,nwid,network->userPtr(),sourceMac,network->mac(),etherType,0,(const void *)frameData,frameLen);
- }
- } else {
- TRACE("dropped FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),(unsigned long long)network->id());
- _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+ const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID);
+ const SharedPtr<Network> network(RR->node->network(nwid));
+ bool trustEstablished = false;
+ if (network) {
+ if (network->gate(tPtr,peer)) {
+ trustEstablished = true;
+ if (size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) {
+ const unsigned int etherType = at<uint16_t>(ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE);
+ const MAC sourceMac(peer->address(),nwid);
+ const unsigned int frameLen = size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD;
+ const uint8_t *const frameData = reinterpret_cast<const uint8_t *>(data()) + ZT_PROTO_VERB_FRAME_IDX_PAYLOAD;
+ if (network->filterIncomingPacket(tPtr,peer,RR->identity.address(),sourceMac,network->mac(),frameData,frameLen,etherType,0) > 0)
+ RR->node->putFrame(tPtr,nwid,network->userPtr(),sourceMac,network->mac(),etherType,0,(const void *)frameData,frameLen);
}
} else {
- TRACE("dropped FRAME from %s(%s): we are not a member of network %.16llx",source().toString().c_str(),_path->address().toString().c_str(),at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID));
_sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+ RR->t->incomingNetworkAccessDenied(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_FRAME,true);
}
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_FRAME,0,Packet::VERB_NOP,trustEstablished);
- } catch ( ... ) {
- TRACE("dropped FRAME from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
+ } else {
+ _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
}
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_FRAME,0,Packet::VERB_NOP,trustEstablished,nwid);
+
return true;
}
bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_EXT_FRAME_IDX_NETWORK_ID);
- const SharedPtr<Network> network(RR->node->network(nwid));
- if (network) {
- const unsigned int flags = (*this)[ZT_PROTO_VERB_EXT_FRAME_IDX_FLAGS];
-
- unsigned int comLen = 0;
- if ((flags & 0x01) != 0) { // inline COM with EXT_FRAME is deprecated but still used with old peers
- CertificateOfMembership com;
- comLen = com.deserialize(*this,ZT_PROTO_VERB_EXT_FRAME_IDX_COM);
- if (com)
- network->addCredential(tPtr,com);
- }
+ const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_EXT_FRAME_IDX_NETWORK_ID);
+ const SharedPtr<Network> network(RR->node->network(nwid));
+ if (network) {
+ const unsigned int flags = (*this)[ZT_PROTO_VERB_EXT_FRAME_IDX_FLAGS];
+
+ unsigned int comLen = 0;
+ if ((flags & 0x01) != 0) { // inline COM with EXT_FRAME is deprecated but still used with old peers
+ CertificateOfMembership com;
+ comLen = com.deserialize(*this,ZT_PROTO_VERB_EXT_FRAME_IDX_COM);
+ if (com)
+ network->addCredential(tPtr,com);
+ }
- if (!network->gate(tPtr,peer)) {
- TRACE("dropped EXT_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),network->id());
- _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,false);
- return true;
- }
+ if (!network->gate(tPtr,peer)) {
+ RR->t->incomingNetworkAccessDenied(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,true);
+ _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,false,nwid);
+ return true;
+ }
- if (size() > ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD) {
- const unsigned int etherType = at<uint16_t>(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_ETHERTYPE);
- const MAC to(field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_TO,ZT_PROTO_VERB_EXT_FRAME_LEN_TO),ZT_PROTO_VERB_EXT_FRAME_LEN_TO);
- const MAC from(field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_FROM,ZT_PROTO_VERB_EXT_FRAME_LEN_FROM),ZT_PROTO_VERB_EXT_FRAME_LEN_FROM);
- const unsigned int frameLen = size() - (comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD);
- const uint8_t *const frameData = (const uint8_t *)field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD,frameLen);
+ if (size() > ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD) {
+ const unsigned int etherType = at<uint16_t>(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_ETHERTYPE);
+ const MAC to(field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_TO,ZT_PROTO_VERB_EXT_FRAME_LEN_TO),ZT_PROTO_VERB_EXT_FRAME_LEN_TO);
+ const MAC from(field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_FROM,ZT_PROTO_VERB_EXT_FRAME_LEN_FROM),ZT_PROTO_VERB_EXT_FRAME_LEN_FROM);
+ const unsigned int frameLen = size() - (comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD);
+ const uint8_t *const frameData = (const uint8_t *)field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD,frameLen);
- if ((!from)||(from.isMulticast())||(from == network->mac())) {
- TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: invalid source MAC %s",from.toString().c_str(),peer->address().toString().c_str(),_path->address().toString().c_str(),to.toString().c_str(),from.toString().c_str());
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
- return true;
- }
+ if ((!from)||(from == network->mac())) {
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+ return true;
+ }
- switch (network->filterIncomingPacket(tPtr,peer,RR->identity.address(),from,to,frameData,frameLen,etherType,0)) {
- case 1:
- if (from != MAC(peer->address(),nwid)) {
- if (network->config().permitsBridging(peer->address())) {
- network->learnBridgeRoute(from,peer->address());
- } else {
- TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: sender not allowed to bridge into %.16llx",from.toString().c_str(),peer->address().toString().c_str(),_path->address().toString().c_str(),to.toString().c_str(),network->id());
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
- return true;
- }
- } else if (to != network->mac()) {
- if (to.isMulticast()) {
- if (network->config().multicastLimit == 0) {
- TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: network %.16llx does not allow multicast",from.toString().c_str(),peer->address().toString().c_str(),_path->address().toString().c_str(),to.toString().c_str(),network->id());
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
- return true;
- }
- } else if (!network->config().permitsBridging(RR->identity.address())) {
- TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: I cannot bridge to %.16llx or bridging disabled on network",from.toString().c_str(),peer->address().toString().c_str(),_path->address().toString().c_str(),to.toString().c_str(),network->id());
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
+ switch (network->filterIncomingPacket(tPtr,peer,RR->identity.address(),from,to,frameData,frameLen,etherType,0)) {
+ case 1:
+ if (from != MAC(peer->address(),nwid)) {
+ if (network->config().permitsBridging(peer->address())) {
+ network->learnBridgeRoute(from,peer->address());
+ } else {
+ RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"bridging not allowed (remote)");
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+ return true;
+ }
+ } else if (to != network->mac()) {
+ if (to.isMulticast()) {
+ if (network->config().multicastLimit == 0) {
+ RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"multicast disabled");
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
return true;
}
+ } else if (!network->config().permitsBridging(RR->identity.address())) {
+ RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"bridging not allowed (local)");
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+ return true;
}
- // fall through -- 2 means accept regardless of bridging checks or other restrictions
- case 2:
- RR->node->putFrame(tPtr,nwid,network->userPtr(),from,to,etherType,0,(const void *)frameData,frameLen);
- break;
- }
- }
-
- if ((flags & 0x10) != 0) { // ACK requested
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
- outp.append((uint8_t)Packet::VERB_EXT_FRAME);
- outp.append((uint64_t)packetId());
- outp.append((uint64_t)nwid);
- outp.armor(peer->key(),true,_path->nextOutgoingCounter());
- _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
+ }
+ // fall through -- 2 means accept regardless of bridging checks or other restrictions
+ case 2:
+ RR->node->putFrame(tPtr,nwid,network->userPtr(),from,to,etherType,0,(const void *)frameData,frameLen);
+ break;
}
+ }
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true);
- } else {
- TRACE("dropped EXT_FRAME from %s(%s): we are not connected to network %.16llx",source().toString().c_str(),_path->address().toString().c_str(),at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID));
- _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,false);
+ if ((flags & 0x10) != 0) { // ACK requested
+ Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
+ outp.append((uint8_t)Packet::VERB_EXT_FRAME);
+ outp.append((uint64_t)packetId());
+ outp.append((uint64_t)nwid);
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
}
- } catch ( ... ) {
- TRACE("dropped EXT_FRAME from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid);
+ } else {
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,false,nwid);
}
+
return true;
}
bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- if (!peer->rateGateEchoRequest(RR->node->now())) {
- TRACE("dropped ECHO from %s(%s): rate limit circuit breaker tripped",source().toString().c_str(),_path->address().toString().c_str());
- return true;
- }
+ if (!peer->rateGateEchoRequest(RR->node->now()))
+ return true;
- const uint64_t pid = packetId();
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
- outp.append((unsigned char)Packet::VERB_ECHO);
- outp.append((uint64_t)pid);
- if (size() > ZT_PACKET_IDX_PAYLOAD)
- outp.append(reinterpret_cast<const unsigned char *>(data()) + ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD);
- outp.armor(peer->key(),true,_path->nextOutgoingCounter());
- _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
+ const uint64_t pid = packetId();
+ Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
+ outp.append((unsigned char)Packet::VERB_ECHO);
+ outp.append((uint64_t)pid);
+ if (size() > ZT_PACKET_IDX_PAYLOAD)
+ outp.append(reinterpret_cast<const unsigned char *>(data()) + ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD);
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
+
+ peer->received(tPtr,_path,hops(),pid,Packet::VERB_ECHO,0,Packet::VERB_NOP,false,0);
- peer->received(tPtr,_path,hops(),pid,Packet::VERB_ECHO,0,Packet::VERB_NOP,false);
- } catch ( ... ) {
- TRACE("dropped ECHO from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
- }
return true;
}
bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- const uint64_t now = RR->node->now();
+ const int64_t now = RR->node->now();
- uint64_t authOnNetwork[256]; // cache for approved network IDs
- unsigned int authOnNetworkCount = 0;
- SharedPtr<Network> network;
- bool trustEstablished = false;
+ uint64_t authOnNetwork[256]; // cache for approved network IDs
+ unsigned int authOnNetworkCount = 0;
+ SharedPtr<Network> network;
+ bool trustEstablished = false;
- // Iterate through 18-byte network,MAC,ADI tuples
- for(unsigned int ptr=ZT_PACKET_IDX_PAYLOAD;ptr<size();ptr+=18) {
- const uint64_t nwid = at<uint64_t>(ptr);
+ // Iterate through 18-byte network,MAC,ADI tuples
+ for(unsigned int ptr=ZT_PACKET_IDX_PAYLOAD;ptr<size();ptr+=18) {
+ const uint64_t nwid = at<uint64_t>(ptr);
- bool auth = false;
- for(unsigned int i=0;i<authOnNetworkCount;++i) {
- if (nwid == authOnNetwork[i]) {
- auth = true;
- break;
- }
- }
- if (!auth) {
- if ((!network)||(network->id() != nwid))
- network = RR->node->network(nwid);
- const bool authOnNet = ((network)&&(network->gate(tPtr,peer)));
- if (!authOnNet)
- _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
- trustEstablished |= authOnNet;
- if (authOnNet||RR->mc->cacheAuthorized(peer->address(),nwid,now)) {
- auth = true;
- if (authOnNetworkCount < 256) // sanity check, packets can't really be this big
- authOnNetwork[authOnNetworkCount++] = nwid;
- }
+ bool auth = false;
+ for(unsigned int i=0;i<authOnNetworkCount;++i) {
+ if (nwid == authOnNetwork[i]) {
+ auth = true;
+ break;
}
-
- if (auth) {
- const MulticastGroup group(MAC(field(ptr + 8,6),6),at<uint32_t>(ptr + 14));
- RR->mc->add(tPtr,now,nwid,group,peer->address());
+ }
+ if (!auth) {
+ if ((!network)||(network->id() != nwid))
+ network = RR->node->network(nwid);
+ const bool authOnNet = ((network)&&(network->gate(tPtr,peer)));
+ if (!authOnNet)
+ _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+ trustEstablished |= authOnNet;
+ if (authOnNet||RR->mc->cacheAuthorized(peer->address(),nwid,now)) {
+ auth = true;
+ if (authOnNetworkCount < 256) // sanity check, packets can't really be this big
+ authOnNetwork[authOnNetworkCount++] = nwid;
}
}
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_LIKE,0,Packet::VERB_NOP,trustEstablished);
- } catch ( ... ) {
- TRACE("dropped MULTICAST_LIKE from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
+ if (auth) {
+ const MulticastGroup group(MAC(field(ptr + 8,6),6),at<uint32_t>(ptr + 14));
+ RR->mc->add(tPtr,now,nwid,group,peer->address());
+ }
}
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_LIKE,0,Packet::VERB_NOP,trustEstablished,(network) ? network->id() : 0);
+
return true;
}
bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- if (!peer->rateGateCredentialsReceived(RR->node->now())) {
- TRACE("dropped NETWORK_CREDENTIALS from %s(%s): rate limit circuit breaker tripped",source().toString().c_str(),_path->address().toString().c_str());
- return true;
- }
-
- CertificateOfMembership com;
- Capability cap;
- Tag tag;
- Revocation revocation;
- CertificateOfOwnership coo;
- bool trustEstablished = false;
+ if (!peer->rateGateCredentialsReceived(RR->node->now()))
+ return true;
- unsigned int p = ZT_PACKET_IDX_PAYLOAD;
- while ((p < size())&&((*this)[p] != 0)) {
- p += com.deserialize(*this,p);
- if (com) {
- const SharedPtr<Network> network(RR->node->network(com.networkId()));
- if (network) {
- switch (network->addCredential(tPtr,com)) {
- case Membership::ADD_REJECTED:
- break;
- case Membership::ADD_ACCEPTED_NEW:
- case Membership::ADD_ACCEPTED_REDUNDANT:
- trustEstablished = true;
- break;
- case Membership::ADD_DEFERRED_FOR_WHOIS:
- return false;
- }
- } else RR->mc->addCredential(tPtr,com,false);
- }
+ CertificateOfMembership com;
+ Capability cap;
+ Tag tag;
+ Revocation revocation;
+ CertificateOfOwnership coo;
+ bool trustEstablished = false;
+ SharedPtr<Network> network;
+
+ unsigned int p = ZT_PACKET_IDX_PAYLOAD;
+ while ((p < size())&&((*this)[p] != 0)) {
+ p += com.deserialize(*this,p);
+ if (com) {
+ network = RR->node->network(com.networkId());
+ if (network) {
+ switch (network->addCredential(tPtr,com)) {
+ case Membership::ADD_REJECTED:
+ break;
+ case Membership::ADD_ACCEPTED_NEW:
+ case Membership::ADD_ACCEPTED_REDUNDANT:
+ trustEstablished = true;
+ break;
+ case Membership::ADD_DEFERRED_FOR_WHOIS:
+ return false;
+ }
+ } else RR->mc->addCredential(tPtr,com,false);
}
- ++p; // skip trailing 0 after COMs if present
-
- if (p < size()) { // older ZeroTier versions do not send capabilities, tags, or revocations
- const unsigned int numCapabilities = at<uint16_t>(p); p += 2;
- for(unsigned int i=0;i<numCapabilities;++i) {
- p += cap.deserialize(*this,p);
- const SharedPtr<Network> network(RR->node->network(cap.networkId()));
- if (network) {
- switch (network->addCredential(tPtr,cap)) {
- case Membership::ADD_REJECTED:
- break;
- case Membership::ADD_ACCEPTED_NEW:
- case Membership::ADD_ACCEPTED_REDUNDANT:
- trustEstablished = true;
- break;
- case Membership::ADD_DEFERRED_FOR_WHOIS:
- return false;
- }
+ }
+ ++p; // skip trailing 0 after COMs if present
+
+ if (p < size()) { // older ZeroTier versions do not send capabilities, tags, or revocations
+ const unsigned int numCapabilities = at<uint16_t>(p); p += 2;
+ for(unsigned int i=0;i<numCapabilities;++i) {
+ p += cap.deserialize(*this,p);
+ if ((!network)||(network->id() != cap.networkId()))
+ network = RR->node->network(cap.networkId());
+ if (network) {
+ switch (network->addCredential(tPtr,cap)) {
+ case Membership::ADD_REJECTED:
+ break;
+ case Membership::ADD_ACCEPTED_NEW:
+ case Membership::ADD_ACCEPTED_REDUNDANT:
+ trustEstablished = true;
+ break;
+ case Membership::ADD_DEFERRED_FOR_WHOIS:
+ return false;
}
}
+ }
- if (p >= size()) return true;
-
- const unsigned int numTags = at<uint16_t>(p); p += 2;
- for(unsigned int i=0;i<numTags;++i) {
- p += tag.deserialize(*this,p);
- const SharedPtr<Network> network(RR->node->network(tag.networkId()));
- if (network) {
- switch (network->addCredential(tPtr,tag)) {
- case Membership::ADD_REJECTED:
- break;
- case Membership::ADD_ACCEPTED_NEW:
- case Membership::ADD_ACCEPTED_REDUNDANT:
- trustEstablished = true;
- break;
- case Membership::ADD_DEFERRED_FOR_WHOIS:
- return false;
- }
+ if (p >= size()) return true;
+
+ const unsigned int numTags = at<uint16_t>(p); p += 2;
+ for(unsigned int i=0;i<numTags;++i) {
+ p += tag.deserialize(*this,p);
+ if ((!network)||(network->id() != tag.networkId()))
+ network = RR->node->network(tag.networkId());
+ if (network) {
+ switch (network->addCredential(tPtr,tag)) {
+ case Membership::ADD_REJECTED:
+ break;
+ case Membership::ADD_ACCEPTED_NEW:
+ case Membership::ADD_ACCEPTED_REDUNDANT:
+ trustEstablished = true;
+ break;
+ case Membership::ADD_DEFERRED_FOR_WHOIS:
+ return false;
}
}
+ }
- if (p >= size()) return true;
-
- const unsigned int numRevocations = at<uint16_t>(p); p += 2;
- for(unsigned int i=0;i<numRevocations;++i) {
- p += revocation.deserialize(*this,p);
- const SharedPtr<Network> network(RR->node->network(revocation.networkId()));
- if (network) {
- switch(network->addCredential(tPtr,peer->address(),revocation)) {
- case Membership::ADD_REJECTED:
- break;
- case Membership::ADD_ACCEPTED_NEW:
- case Membership::ADD_ACCEPTED_REDUNDANT:
- trustEstablished = true;
- break;
- case Membership::ADD_DEFERRED_FOR_WHOIS:
- return false;
- }
+ if (p >= size()) return true;
+
+ const unsigned int numRevocations = at<uint16_t>(p); p += 2;
+ for(unsigned int i=0;i<numRevocations;++i) {
+ p += revocation.deserialize(*this,p);
+ if ((!network)||(network->id() != revocation.networkId()))
+ network = RR->node->network(revocation.networkId());
+ if (network) {
+ switch(network->addCredential(tPtr,peer->address(),revocation)) {
+ case Membership::ADD_REJECTED:
+ break;
+ case Membership::ADD_ACCEPTED_NEW:
+ case Membership::ADD_ACCEPTED_REDUNDANT:
+ trustEstablished = true;
+ break;
+ case Membership::ADD_DEFERRED_FOR_WHOIS:
+ return false;
}
}
+ }
- if (p >= size()) return true;
-
- const unsigned int numCoos = at<uint16_t>(p); p += 2;
- for(unsigned int i=0;i<numCoos;++i) {
- p += coo.deserialize(*this,p);
- const SharedPtr<Network> network(RR->node->network(coo.networkId()));
- if (network) {
- switch(network->addCredential(tPtr,coo)) {
- case Membership::ADD_REJECTED:
- break;
- case Membership::ADD_ACCEPTED_NEW:
- case Membership::ADD_ACCEPTED_REDUNDANT:
- trustEstablished = true;
- break;
- case Membership::ADD_DEFERRED_FOR_WHOIS:
- return false;
- }
+ if (p >= size()) return true;
+
+ const unsigned int numCoos = at<uint16_t>(p); p += 2;
+ for(unsigned int i=0;i<numCoos;++i) {
+ p += coo.deserialize(*this,p);
+ if ((!network)||(network->id() != coo.networkId()))
+ network = RR->node->network(coo.networkId());
+ if (network) {
+ switch(network->addCredential(tPtr,coo)) {
+ case Membership::ADD_REJECTED:
+ break;
+ case Membership::ADD_ACCEPTED_NEW:
+ case Membership::ADD_ACCEPTED_REDUNDANT:
+ trustEstablished = true;
+ break;
+ case Membership::ADD_DEFERRED_FOR_WHOIS:
+ return false;
}
}
}
-
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_NETWORK_CREDENTIALS,0,Packet::VERB_NOP,trustEstablished);
- } catch (std::exception &exc) {
- //fprintf(stderr,"dropped NETWORK_CREDENTIALS from %s(%s): %s" ZT_EOL_S,source().toString().c_str(),_path->address().toString().c_str(),exc.what());
- TRACE("dropped NETWORK_CREDENTIALS from %s(%s): %s",source().toString().c_str(),_path->address().toString().c_str(),exc.what());
- } catch ( ... ) {
- //fprintf(stderr,"dropped NETWORK_CREDENTIALS from %s(%s): unknown exception" ZT_EOL_S,source().toString().c_str(),_path->address().toString().c_str());
- TRACE("dropped NETWORK_CREDENTIALS from %s(%s): unknown exception",source().toString().c_str(),_path->address().toString().c_str());
}
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_NETWORK_CREDENTIALS,0,Packet::VERB_NOP,trustEstablished,(network) ? network->id() : 0);
+
return true;
}
bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID);
- const unsigned int hopCount = hops();
- const uint64_t requestPacketId = packetId();
-
- if (RR->localNetworkController) {
- const unsigned int metaDataLength = (ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN <= size()) ? at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN) : 0;
- const char *metaDataBytes = (metaDataLength != 0) ? (const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT,metaDataLength) : (const char *)0;
- const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> metaData(metaDataBytes,metaDataLength);
- RR->localNetworkController->request(nwid,(hopCount > 0) ? InetAddress() : _path->address(),requestPacketId,peer->identity(),metaData);
- } else {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
- outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
- outp.append(requestPacketId);
- outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION);
- outp.append(nwid);
- outp.armor(peer->key(),true,_path->nextOutgoingCounter());
- _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
- }
-
- peer->received(tPtr,_path,hopCount,requestPacketId,Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP,false);
- } catch (std::exception &exc) {
- //fprintf(stderr,"dropped NETWORK_CONFIG_REQUEST from %s(%s): %s" ZT_EOL_S,source().toString().c_str(),_path->address().toString().c_str(),exc.what());
- TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): %s",source().toString().c_str(),_path->address().toString().c_str(),exc.what());
- } catch ( ... ) {
- //fprintf(stderr,"dropped NETWORK_CONFIG_REQUEST from %s(%s): unknown exception" ZT_EOL_S,source().toString().c_str(),_path->address().toString().c_str());
- TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): unknown exception",source().toString().c_str(),_path->address().toString().c_str());
+ const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID);
+ const unsigned int hopCount = hops();
+ const uint64_t requestPacketId = packetId();
+
+ if (RR->localNetworkController) {
+ const unsigned int metaDataLength = (ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN <= size()) ? at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN) : 0;
+ const char *metaDataBytes = (metaDataLength != 0) ? (const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT,metaDataLength) : (const char *)0;
+ const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> metaData(metaDataBytes,metaDataLength);
+ RR->localNetworkController->request(nwid,(hopCount > 0) ? InetAddress() : _path->address(),requestPacketId,peer->identity(),metaData);
+ } else {
+ Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
+ outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
+ outp.append(requestPacketId);
+ outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION);
+ outp.append(nwid);
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
}
+
+ peer->received(tPtr,_path,hopCount,requestPacketId,Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP,false,nwid);
+
return true;
}
bool IncomingPacket::_doNETWORK_CONFIG(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PACKET_IDX_PAYLOAD)));
- if (network) {
- const uint64_t configUpdateId = network->handleConfigChunk(tPtr,packetId(),source(),*this,ZT_PACKET_IDX_PAYLOAD);
- if (configUpdateId) {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
- outp.append((uint8_t)Packet::VERB_ECHO);
- outp.append((uint64_t)packetId());
- outp.append((uint64_t)network->id());
- outp.append((uint64_t)configUpdateId);
- outp.armor(peer->key(),true,_path->nextOutgoingCounter());
- _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
- }
+ const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PACKET_IDX_PAYLOAD)));
+ if (network) {
+ const uint64_t configUpdateId = network->handleConfigChunk(tPtr,packetId(),source(),*this,ZT_PACKET_IDX_PAYLOAD);
+ if (configUpdateId) {
+ Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
+ outp.append((uint8_t)Packet::VERB_ECHO);
+ outp.append((uint64_t)packetId());
+ outp.append((uint64_t)network->id());
+ outp.append((uint64_t)configUpdateId);
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
}
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_NETWORK_CONFIG,0,Packet::VERB_NOP,false);
- } catch ( ... ) {
- TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
}
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_NETWORK_CONFIG,0,Packet::VERB_NOP,false,(network) ? network->id() : 0);
+
return true;
}
bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_NETWORK_ID);
- const unsigned int flags = (*this)[ZT_PROTO_VERB_MULTICAST_GATHER_IDX_FLAGS];
- const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI));
- const unsigned int gatherLimit = at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT);
-
- //TRACE("<<MC %s(%s) GATHER up to %u in %.16llx/%s",source().toString().c_str(),_path->address().toString().c_str(),gatherLimit,nwid,mg.toString().c_str());
-
- const SharedPtr<Network> network(RR->node->network(nwid));
+ const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_NETWORK_ID);
+ const unsigned int flags = (*this)[ZT_PROTO_VERB_MULTICAST_GATHER_IDX_FLAGS];
+ const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI));
+ const unsigned int gatherLimit = at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT);
- if ((flags & 0x01) != 0) {
- try {
- CertificateOfMembership com;
- com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_GATHER_IDX_COM);
- if (com) {
- if (network)
- network->addCredential(tPtr,com);
- else RR->mc->addCredential(tPtr,com,false);
- }
- } catch ( ... ) {
- TRACE("MULTICAST_GATHER from %s(%s): discarded invalid COM",peer->address().toString().c_str(),_path->address().toString().c_str());
- }
- }
+ const SharedPtr<Network> network(RR->node->network(nwid));
- const bool trustEstablished = ((network)&&(network->gate(tPtr,peer)));
- if (!trustEstablished)
- _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
- if ( ( trustEstablished || RR->mc->cacheAuthorized(peer->address(),nwid,RR->node->now()) ) && (gatherLimit > 0) ) {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
- outp.append((unsigned char)Packet::VERB_MULTICAST_GATHER);
- outp.append(packetId());
- outp.append(nwid);
- mg.mac().appendTo(outp);
- outp.append((uint32_t)mg.adi());
- const unsigned int gatheredLocally = RR->mc->gather(peer->address(),nwid,mg,outp,gatherLimit);
- if (gatheredLocally > 0) {
- outp.armor(peer->key(),true,_path->nextOutgoingCounter());
- _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
+ if ((flags & 0x01) != 0) {
+ try {
+ CertificateOfMembership com;
+ com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_GATHER_IDX_COM);
+ if (com) {
+ if (network)
+ network->addCredential(tPtr,com);
+ else RR->mc->addCredential(tPtr,com,false);
}
+ } catch ( ... ) {} // discard invalid COMs
+ }
- // If we are a member of a cluster, distribute this GATHER across it
-#ifdef ZT_ENABLE_CLUSTER
- if ((RR->cluster)&&(gatheredLocally < gatherLimit))
- RR->cluster->sendDistributedQuery(*this);
-#endif
+ const bool trustEstablished = ((network)&&(network->gate(tPtr,peer)));
+ if (!trustEstablished)
+ _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+ if ( ( trustEstablished || RR->mc->cacheAuthorized(peer->address(),nwid,RR->node->now()) ) && (gatherLimit > 0) ) {
+ Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
+ outp.append((unsigned char)Packet::VERB_MULTICAST_GATHER);
+ outp.append(packetId());
+ outp.append(nwid);
+ mg.mac().appendTo(outp);
+ outp.append((uint32_t)mg.adi());
+ const unsigned int gatheredLocally = RR->mc->gather(peer->address(),nwid,mg,outp,gatherLimit);
+ if (gatheredLocally > 0) {
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
}
-
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_GATHER,0,Packet::VERB_NOP,trustEstablished);
- } catch ( ... ) {
- TRACE("dropped MULTICAST_GATHER from %s(%s): unexpected exception",peer->address().toString().c_str(),_path->address().toString().c_str());
}
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_GATHER,0,Packet::VERB_NOP,trustEstablished,nwid);
+
return true;
}
bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID);
- const unsigned int flags = (*this)[ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FLAGS];
-
- const SharedPtr<Network> network(RR->node->network(nwid));
- if (network) {
- // Offset -- size of optional fields added to position of later fields
- unsigned int offset = 0;
-
- if ((flags & 0x01) != 0) {
- // This is deprecated but may still be sent by old peers
- CertificateOfMembership com;
- offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME_IDX_COM);
- if (com)
- network->addCredential(tPtr,com);
- }
-
- if (!network->gate(tPtr,peer)) {
- TRACE("dropped MULTICAST_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),(unsigned long long)network->id());
- _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false);
- return true;
- }
-
- if (network->config().multicastLimit == 0) {
- TRACE("dropped MULTICAST_FRAME from %s(%s): network %.16llx does not allow multicast",peer->address().toString().c_str(),_path->address().toString().c_str(),(unsigned long long)network->id());
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false);
- return true;
- }
+ const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID);
+ const unsigned int flags = (*this)[ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FLAGS];
- unsigned int gatherLimit = 0;
- if ((flags & 0x02) != 0) {
- gatherLimit = at<uint32_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_GATHER_LIMIT);
- offset += 4;
- }
+ const SharedPtr<Network> network(RR->node->network(nwid));
+ if (network) {
+ // Offset -- size of optional fields added to position of later fields
+ unsigned int offset = 0;
- MAC from;
- if ((flags & 0x04) != 0) {
- from.setTo(field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SOURCE_MAC,6),6);
- offset += 6;
- } else {
- from.fromAddress(peer->address(),nwid);
- }
-
- const MulticastGroup to(MAC(field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DEST_MAC,6),6),at<uint32_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DEST_ADI));
- const unsigned int etherType = at<uint16_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_ETHERTYPE);
- const unsigned int frameLen = size() - (offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME);
-
- //TRACE("<<MC FRAME %.16llx/%s from %s@%s flags %.2x length %u",nwid,to.toString().c_str(),from.toString().c_str(),peer->address().toString().c_str(),flags,frameLen);
-
- if ((frameLen > 0)&&(frameLen <= ZT_IF_MTU)) {
- if (!to.mac().isMulticast()) {
- TRACE("dropped MULTICAST_FRAME from %s@%s(%s) to %s: destination is unicast, must use FRAME or EXT_FRAME",from.toString().c_str(),peer->address().toString().c_str(),_path->address().toString().c_str(),to.toString().c_str());
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
- return true;
- }
- if ((!from)||(from.isMulticast())||(from == network->mac())) {
- TRACE("dropped MULTICAST_FRAME from %s@%s(%s) to %s: invalid source MAC",from.toString().c_str(),peer->address().toString().c_str(),_path->address().toString().c_str(),to.toString().c_str());
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
- return true;
- }
-
- if (from != MAC(peer->address(),nwid)) {
- if (network->config().permitsBridging(peer->address())) {
- network->learnBridgeRoute(from,peer->address());
- } else {
- TRACE("dropped MULTICAST_FRAME from %s@%s(%s) to %s: sender not allowed to bridge into %.16llx",from.toString().c_str(),peer->address().toString().c_str(),_path->address().toString().c_str(),to.toString().c_str(),network->id());
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
- return true;
- }
- }
-
- const uint8_t *const frameData = (const uint8_t *)field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME,frameLen);
- if (network->filterIncomingPacket(tPtr,peer,RR->identity.address(),from,to.mac(),frameData,frameLen,etherType,0) > 0) {
- RR->node->putFrame(tPtr,nwid,network->userPtr(),from,to.mac(),etherType,0,(const void *)frameData,frameLen);
- }
- }
-
- if (gatherLimit) {
- Packet outp(source(),RR->identity.address(),Packet::VERB_OK);
- outp.append((unsigned char)Packet::VERB_MULTICAST_FRAME);
- outp.append(packetId());
- outp.append(nwid);
- to.mac().appendTo(outp);
- outp.append((uint32_t)to.adi());
- outp.append((unsigned char)0x02); // flag 0x02 = contains gather results
- if (RR->mc->gather(peer->address(),nwid,to,outp,gatherLimit)) {
- outp.armor(peer->key(),true,_path->nextOutgoingCounter());
- _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
- }
- }
-
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true);
- } else {
- _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false);
+ if ((flags & 0x01) != 0) {
+ // This is deprecated but may still be sent by old peers
+ CertificateOfMembership com;
+ offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME_IDX_COM);
+ if (com)
+ network->addCredential(tPtr,com);
}
- } catch ( ... ) {
- TRACE("dropped MULTICAST_FRAME from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
- }
- return true;
-}
-bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
-{
- try {
- const uint64_t now = RR->node->now();
-
- // First, subject this to a rate limit
- if (!peer->rateGatePushDirectPaths(now)) {
- TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): circuit breaker tripped",source().toString().c_str(),_path->address().toString().c_str());
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,false);
+ if (!network->gate(tPtr,peer)) {
+ RR->t->incomingNetworkAccessDenied(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_MULTICAST_FRAME,true);
+ _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false,nwid);
return true;
}
- // Second, limit addresses by scope and type
- uint8_t countPerScope[ZT_INETADDRESS_MAX_SCOPE+1][2]; // [][0] is v4, [][1] is v6
- memset(countPerScope,0,sizeof(countPerScope));
-
- unsigned int count = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD);
- unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2;
-
- while (count--) { // if ptr overflows Buffer will throw
- // TODO: some flags are not yet implemented
-
- unsigned int flags = (*this)[ptr++];
- unsigned int extLen = at<uint16_t>(ptr); ptr += 2;
- ptr += extLen; // unused right now
- unsigned int addrType = (*this)[ptr++];
- unsigned int addrLen = (*this)[ptr++];
-
- switch(addrType) {
- case 4: {
- const InetAddress a(field(ptr,4),4,at<uint16_t>(ptr + 4));
- if (
- ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && // not being told to forget
- (!( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) == 0) && (peer->hasActivePathTo(now,a)) )) && // not already known
- (RR->node->shouldUsePathForZeroTierTraffic(tPtr,peer->address(),_path->localAddress(),a)) ) // should use path
- {
- if ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) != 0)
- peer->setClusterPreferred(a);
- if (++countPerScope[(int)a.ipScope()][0] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) {
- TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str());
- peer->attemptToContactAt(tPtr,InetAddress(),a,now,false,0);
- } else {
- TRACE("ignoring contact for %s at %s -- too many per scope",peer->address().toString().c_str(),a.toString().c_str());
- }
- }
- } break;
- case 6: {
- const InetAddress a(field(ptr,16),16,at<uint16_t>(ptr + 16));
- if (
- ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && // not being told to forget
- (!( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) == 0) && (peer->hasActivePathTo(now,a)) )) && // not already known
- (RR->node->shouldUsePathForZeroTierTraffic(tPtr,peer->address(),_path->localAddress(),a)) ) // should use path
- {
- if ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) != 0)
- peer->setClusterPreferred(a);
- if (++countPerScope[(int)a.ipScope()][1] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) {
- TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str());
- peer->attemptToContactAt(tPtr,InetAddress(),a,now,false,0);
- } else {
- TRACE("ignoring contact for %s at %s -- too many per scope",peer->address().toString().c_str(),a.toString().c_str());
- }
- }
- } break;
- }
- ptr += addrLen;
+ unsigned int gatherLimit = 0;
+ if ((flags & 0x02) != 0) {
+ gatherLimit = at<uint32_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_GATHER_LIMIT);
+ offset += 4;
}
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,false);
- } catch ( ... ) {
- TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
- }
- return true;
-}
-
-bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
-{
- try {
- const Address originatorAddress(field(ZT_PACKET_IDX_PAYLOAD,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
- SharedPtr<Peer> originator(RR->topology->getPeer(tPtr,originatorAddress));
- if (!originator) {
- RR->sw->requestWhois(tPtr,originatorAddress);
- return false;
- }
-
- const unsigned int flags = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 5);
- const uint64_t timestamp = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 7);
- const uint64_t testId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 15);
-
- // Tracks total length of variable length fields, initialized to originator credential length below
- unsigned int vlf;
-
- // Originator credentials -- right now only a network ID for which the originator is controller or is authorized by controller is allowed
- const unsigned int originatorCredentialLength = vlf = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 23);
- uint64_t originatorCredentialNetworkId = 0;
- if (originatorCredentialLength >= 1) {
- switch((*this)[ZT_PACKET_IDX_PAYLOAD + 25]) {
- case 0x01: { // 64-bit network ID, originator must be controller
- if (originatorCredentialLength >= 9)
- originatorCredentialNetworkId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 26);
- } break;
- default: break;
- }
+ MAC from;
+ if ((flags & 0x04) != 0) {
+ from.setTo(field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SOURCE_MAC,6),6);
+ offset += 6;
+ } else {
+ from.fromAddress(peer->address(),nwid);
}
- // Add length of "additional fields," which are currently unused
- vlf += at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 25 + vlf);
+ const MulticastGroup to(MAC(field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DEST_MAC,6),6),at<uint32_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DEST_ADI));
+ const unsigned int etherType = at<uint16_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_ETHERTYPE);
+ const unsigned int frameLen = size() - (offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME);
- // Verify signature -- only tests signed by their originators are allowed
- const unsigned int signatureLength = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 27 + vlf);
- if (!originator->identity().verify(field(ZT_PACKET_IDX_PAYLOAD,27 + vlf),27 + vlf,field(ZT_PACKET_IDX_PAYLOAD + 29 + vlf,signatureLength),signatureLength)) {
- TRACE("dropped CIRCUIT_TEST from %s(%s): signature by originator %s invalid",source().toString().c_str(),_path->address().toString().c_str(),originatorAddress.toString().c_str());
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP,false);
+ if (network->config().multicastLimit == 0) {
+ RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_MULTICAST_FRAME,from,to.mac(),"multicast disabled");
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false,nwid);
return true;
}
- vlf += signatureLength;
- // Save this length so we can copy the immutable parts of this test
- // into the one we send along to next hops.
- const unsigned int lengthOfSignedPortionAndSignature = 29 + vlf;
-
- // Add length of second "additional fields" section.
- vlf += at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 29 + vlf);
-
- uint64_t reportFlags = 0;
-
- // Check credentials (signature already verified)
- if (originatorCredentialNetworkId) {
- SharedPtr<Network> network(RR->node->network(originatorCredentialNetworkId));
- if ((!network)||(!network->config().circuitTestingAllowed(originatorAddress))) {
- TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s specified network ID %.16llx as credential, and we don't belong to that network or originator is not allowed'",source().toString().c_str(),_path->address().toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId);
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP,false);
+ if ((frameLen > 0)&&(frameLen <= ZT_MAX_MTU)) {
+ if (!to.mac().isMulticast()) {
+ RR->t->incomingPacketInvalid(tPtr,_path,packetId(),source(),hops(),Packet::VERB_MULTICAST_FRAME,"destination not multicast");
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
return true;
}
- if (network->gate(tPtr,peer))
- reportFlags |= ZT_CIRCUIT_TEST_REPORT_FLAGS_UPSTREAM_AUTHORIZED_IN_PATH;
- } else {
- TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s did not specify a credential or credential type",source().toString().c_str(),_path->address().toString().c_str(),originatorAddress.toString().c_str());
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP,false);
- return true;
- }
+ if ((!from)||(from.isMulticast())||(from == network->mac())) {
+ RR->t->incomingPacketInvalid(tPtr,_path,packetId(),source(),hops(),Packet::VERB_MULTICAST_FRAME,"invalid source MAC");
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+ return true;
+ }
+
+ const uint8_t *const frameData = (const uint8_t *)field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME,frameLen);
+
+ if ((flags & 0x08)&&(network->config().isMulticastReplicator(RR->identity.address())))
+ RR->mc->send(tPtr,RR->node->now(),network,peer->address(),to,from,etherType,frameData,frameLen);
- const uint64_t now = RR->node->now();
-
- unsigned int breadth = 0;
- Address nextHop[256]; // breadth is a uin8_t, so this is the max
- InetAddress nextHopBestPathAddress[256];
- unsigned int remainingHopsPtr = ZT_PACKET_IDX_PAYLOAD + 33 + vlf;
- if ((ZT_PACKET_IDX_PAYLOAD + 31 + vlf) < size()) {
- // unsigned int nextHopFlags = (*this)[ZT_PACKET_IDX_PAYLOAD + 31 + vlf]
- breadth = (*this)[ZT_PACKET_IDX_PAYLOAD + 32 + vlf];
- for(unsigned int h=0;h<breadth;++h) {
- nextHop[h].setTo(field(remainingHopsPtr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
- remainingHopsPtr += ZT_ADDRESS_LENGTH;
- SharedPtr<Peer> nhp(RR->topology->getPeer(tPtr,nextHop[h]));
- if (nhp) {
- SharedPtr<Path> nhbp(nhp->getBestPath(now,false));
- if ((nhbp)&&(nhbp->alive(now)))
- nextHopBestPathAddress[h] = nhbp->address();
+ if (from != MAC(peer->address(),nwid)) {
+ if (network->config().permitsBridging(peer->address())) {
+ network->learnBridgeRoute(from,peer->address());
+ } else {
+ RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_MULTICAST_FRAME,from,to.mac(),"bridging not allowed (remote)");
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+ return true;
}
}
- }
- // Report back to originator, depending on flags and whether we are last hop
- if ( ((flags & 0x01) != 0) || ((breadth == 0)&&((flags & 0x02) != 0)) ) {
- Packet outp(originatorAddress,RR->identity.address(),Packet::VERB_CIRCUIT_TEST_REPORT);
- outp.append((uint64_t)timestamp);
- outp.append((uint64_t)testId);
- outp.append((uint64_t)0); // field reserved for future use
- outp.append((uint8_t)ZT_VENDOR_ZEROTIER);
- outp.append((uint8_t)ZT_PROTO_VERSION);
- outp.append((uint8_t)ZEROTIER_ONE_VERSION_MAJOR);
- outp.append((uint8_t)ZEROTIER_ONE_VERSION_MINOR);
- outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
- outp.append((uint16_t)ZT_PLATFORM_UNSPECIFIED);
- outp.append((uint16_t)ZT_ARCHITECTURE_UNSPECIFIED);
- outp.append((uint16_t)0); // error code, currently unused
- outp.append((uint64_t)reportFlags);
- outp.append((uint64_t)packetId());
- peer->address().appendTo(outp);
- outp.append((uint8_t)hops());
- _path->localAddress().serialize(outp);
- _path->address().serialize(outp);
- outp.append((uint16_t)_path->linkQuality());
- outp.append((uint8_t)breadth);
- for(unsigned int h=0;h<breadth;++h) {
- nextHop[h].appendTo(outp);
- nextHopBestPathAddress[h].serialize(outp); // appends 0 if null InetAddress
- }
- RR->sw->send(tPtr,outp,true);
+ if (network->filterIncomingPacket(tPtr,peer,RR->identity.address(),from,to.mac(),frameData,frameLen,etherType,0) > 0)
+ RR->node->putFrame(tPtr,nwid,network->userPtr(),from,to.mac(),etherType,0,(const void *)frameData,frameLen);
}
- // If there are next hops, forward the test along through the graph
- if (breadth > 0) {
- Packet outp(Address(),RR->identity.address(),Packet::VERB_CIRCUIT_TEST);
- outp.append(field(ZT_PACKET_IDX_PAYLOAD,lengthOfSignedPortionAndSignature),lengthOfSignedPortionAndSignature);
- outp.append((uint16_t)0); // no additional fields
- if (remainingHopsPtr < size())
- outp.append(field(remainingHopsPtr,size() - remainingHopsPtr),size() - remainingHopsPtr);
-
- for(unsigned int h=0;h<breadth;++h) {
- if (RR->identity.address() != nextHop[h]) { // next hops that loop back to the current hop are not valid
- outp.newInitializationVector();
- outp.setDestination(nextHop[h]);
- RR->sw->send(tPtr,outp,true);
- }
+ if (gatherLimit) {
+ Packet outp(source(),RR->identity.address(),Packet::VERB_OK);
+ outp.append((unsigned char)Packet::VERB_MULTICAST_FRAME);
+ outp.append(packetId());
+ outp.append(nwid);
+ to.mac().appendTo(outp);
+ outp.append((uint32_t)to.adi());
+ outp.append((unsigned char)0x02); // flag 0x02 = contains gather results
+ if (RR->mc->gather(peer->address(),nwid,to,outp,gatherLimit)) {
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
}
}
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP,false);
- } catch ( ... ) {
- TRACE("dropped CIRCUIT_TEST from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid);
+ } else {
+ _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false,nwid);
}
+
return true;
}
-bool IncomingPacket::_doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- ZT_CircuitTestReport report;
- memset(&report,0,sizeof(report));
-
- report.current = peer->address().toInt();
- report.upstream = Address(field(ZT_PACKET_IDX_PAYLOAD + 52,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH).toInt();
- report.testId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 8);
- report.timestamp = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD);
- report.sourcePacketId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 44);
- report.flags = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 36);
- report.sourcePacketHopCount = (*this)[ZT_PACKET_IDX_PAYLOAD + 57]; // end of fixed length headers: 58
- report.errorCode = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 34);
- report.vendor = (enum ZT_Vendor)((*this)[ZT_PACKET_IDX_PAYLOAD + 24]);
- report.protocolVersion = (*this)[ZT_PACKET_IDX_PAYLOAD + 25];
- report.majorVersion = (*this)[ZT_PACKET_IDX_PAYLOAD + 26];
- report.minorVersion = (*this)[ZT_PACKET_IDX_PAYLOAD + 27];
- report.revision = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 28);
- report.platform = (enum ZT_Platform)at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 30);
- report.architecture = (enum ZT_Architecture)at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 32);
-
- const unsigned int receivedOnLocalAddressLen = reinterpret_cast<InetAddress *>(&(report.receivedOnLocalAddress))->deserialize(*this,ZT_PACKET_IDX_PAYLOAD + 58);
- const unsigned int receivedFromRemoteAddressLen = reinterpret_cast<InetAddress *>(&(report.receivedFromRemoteAddress))->deserialize(*this,ZT_PACKET_IDX_PAYLOAD + 58 + receivedOnLocalAddressLen);
- unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 58 + receivedOnLocalAddressLen + receivedFromRemoteAddressLen;
- if (report.protocolVersion >= 9) {
- report.receivedFromLinkQuality = at<uint16_t>(ptr); ptr += 2;
- } else {
- report.receivedFromLinkQuality = ZT_PATH_LINK_QUALITY_MAX;
- ptr += at<uint16_t>(ptr) + 2; // this field was once an 'extended field length' reserved field, which was always set to 0
- }
+ const int64_t now = RR->node->now();
- report.nextHopCount = (*this)[ptr++];
- if (report.nextHopCount > ZT_CIRCUIT_TEST_MAX_HOP_BREADTH) // sanity check, shouldn't be possible
- report.nextHopCount = ZT_CIRCUIT_TEST_MAX_HOP_BREADTH;
- for(unsigned int h=0;h<report.nextHopCount;++h) {
- report.nextHops[h].address = Address(field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH).toInt(); ptr += ZT_ADDRESS_LENGTH;
- ptr += reinterpret_cast<InetAddress *>(&(report.nextHops[h].physicalAddress))->deserialize(*this,ptr);
+ // First, subject this to a rate limit
+ if (!peer->rateGatePushDirectPaths(now)) {
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,false,0);
+ return true;
+ }
+
+ // Second, limit addresses by scope and type
+ uint8_t countPerScope[ZT_INETADDRESS_MAX_SCOPE+1][2]; // [][0] is v4, [][1] is v6
+ memset(countPerScope,0,sizeof(countPerScope));
+
+ unsigned int count = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD);
+ unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2;
+
+ while (count--) { // if ptr overflows Buffer will throw
+ // TODO: some flags are not yet implemented
+
+ unsigned int flags = (*this)[ptr++];
+ unsigned int extLen = at<uint16_t>(ptr); ptr += 2;
+ ptr += extLen; // unused right now
+ unsigned int addrType = (*this)[ptr++];
+ unsigned int addrLen = (*this)[ptr++];
+
+ switch(addrType) {
+ case 4: {
+ const InetAddress a(field(ptr,4),4,at<uint16_t>(ptr + 4));
+ if (
+ ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && // not being told to forget
+ (!( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) == 0) && (peer->hasActivePathTo(now,a)) )) && // not already known
+ (RR->node->shouldUsePathForZeroTierTraffic(tPtr,peer->address(),_path->localSocket(),a)) ) // should use path
+ {
+ if ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) != 0) {
+ peer->clusterRedirect(tPtr,_path,a,now);
+ } else if (++countPerScope[(int)a.ipScope()][0] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) {
+ peer->attemptToContactAt(tPtr,InetAddress(),a,now,false);
+ }
+ }
+ } break;
+ case 6: {
+ const InetAddress a(field(ptr,16),16,at<uint16_t>(ptr + 16));
+ if (
+ ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && // not being told to forget
+ (!( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) == 0) && (peer->hasActivePathTo(now,a)) )) && // not already known
+ (RR->node->shouldUsePathForZeroTierTraffic(tPtr,peer->address(),_path->localSocket(),a)) ) // should use path
+ {
+ if ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) != 0) {
+ peer->clusterRedirect(tPtr,_path,a,now);
+ } else if (++countPerScope[(int)a.ipScope()][1] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) {
+ peer->attemptToContactAt(tPtr,InetAddress(),a,now,false);
+ }
+ }
+ } break;
}
+ ptr += addrLen;
+ }
- RR->node->postCircuitTestReport(&report);
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,false,0);
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST_REPORT,0,Packet::VERB_NOP,false);
- } catch ( ... ) {
- TRACE("dropped CIRCUIT_TEST_REPORT from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
- }
return true;
}
bool IncomingPacket::_doUSER_MESSAGE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- if (size() >= (ZT_PACKET_IDX_PAYLOAD + 8)) {
- ZT_UserMessage um;
- um.origin = peer->address().toInt();
- um.typeId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD);
- um.data = reinterpret_cast<const void *>(reinterpret_cast<const uint8_t *>(data()) + ZT_PACKET_IDX_PAYLOAD + 8);
- um.length = size() - (ZT_PACKET_IDX_PAYLOAD + 8);
- RR->node->postEvent(tPtr,ZT_EVENT_USER_MESSAGE,reinterpret_cast<const void *>(&um));
+ if (likely(size() >= (ZT_PACKET_IDX_PAYLOAD + 8))) {
+ ZT_UserMessage um;
+ um.origin = peer->address().toInt();
+ um.typeId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD);
+ um.data = reinterpret_cast<const void *>(reinterpret_cast<const uint8_t *>(data()) + ZT_PACKET_IDX_PAYLOAD + 8);
+ um.length = size() - (ZT_PACKET_IDX_PAYLOAD + 8);
+ RR->node->postEvent(tPtr,ZT_EVENT_USER_MESSAGE,reinterpret_cast<const void *>(&um));
+ }
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_USER_MESSAGE,0,Packet::VERB_NOP,false,0);
+
+ return true;
+}
+
+bool IncomingPacket::_doREMOTE_TRACE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
+{
+ ZT_RemoteTrace rt;
+ const char *ptr = reinterpret_cast<const char *>(data()) + ZT_PACKET_IDX_PAYLOAD;
+ const char *const eof = reinterpret_cast<const char *>(data()) + size();
+ rt.origin = peer->address().toInt();
+ rt.data = const_cast<char *>(ptr); // start of first string
+ while (ptr < eof) {
+ if (!*ptr) { // end of string
+ rt.len = (unsigned int)(ptr - rt.data);
+ if ((rt.len > 0)&&(rt.len <= ZT_MAX_REMOTE_TRACE_SIZE)) {
+ RR->node->postEvent(tPtr,ZT_EVENT_REMOTE_TRACE,&rt);
+ }
+ rt.data = const_cast<char *>(++ptr); // start of next string, if any
+ } else {
+ ++ptr;
}
- peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST_REPORT,0,Packet::VERB_NOP,false);
- } catch ( ... ) {
- TRACE("dropped CIRCUIT_TEST_REPORT from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
}
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_REMOTE_TRACE,0,Packet::VERB_NOP,false,0);
+
return true;
}
void IncomingPacket::_sendErrorNeedCredentials(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer,const uint64_t nwid)
{
- const uint64_t now = RR->node->now();
+ const int64_t now = RR->node->now();
if (peer->rateGateOutgoingComRequest(now)) {
Packet outp(source(),RR->identity.address(),Packet::VERB_ERROR);
outp.append((uint8_t)verb());
outp.append(packetId());
outp.append((uint8_t)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE);
outp.append(nwid);
- outp.armor(peer->key(),true,_path->nextOutgoingCounter());
+ outp.armor(peer->key(),true);
_path->send(RR,tPtr,outp.data(),outp.size(),now);
}
}
diff --git a/node/IncomingPacket.hpp b/node/IncomingPacket.hpp
index 3d4a2e05..88f4f066 100644
--- a/node/IncomingPacket.hpp
+++ b/node/IncomingPacket.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_INCOMINGPACKET_HPP
@@ -69,7 +77,7 @@ public:
* @param now Current time
* @throws std::out_of_range Range error processing packet
*/
- IncomingPacket(const void *data,unsigned int len,const SharedPtr<Path> &path,uint64_t now) :
+ IncomingPacket(const void *data,unsigned int len,const SharedPtr<Path> &path,int64_t now) :
Packet(data,len),
_receiveTime(now),
_path(path)
@@ -85,7 +93,7 @@ public:
* @param now Current time
* @throws std::out_of_range Range error processing packet
*/
- inline void init(const void *data,unsigned int len,const SharedPtr<Path> &path,uint64_t now)
+ inline void init(const void *data,unsigned int len,const SharedPtr<Path> &path,int64_t now)
{
copyFrom(data,len);
_receiveTime = now;
@@ -110,7 +118,7 @@ public:
/**
* @return Time of packet receipt / start of decode
*/
- inline uint64_t receiveTime() const throw() { return _receiveTime; }
+ inline uint64_t receiveTime() const { return _receiveTime; }
private:
// These are called internally to handle packet contents once it has
@@ -130,9 +138,8 @@ private:
bool _doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
bool _doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
- bool _doCIRCUIT_TEST(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
- bool _doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
bool _doUSER_MESSAGE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doREMOTE_TRACE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
void _sendErrorNeedCredentials(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer,const uint64_t nwid);
diff --git a/node/InetAddress.cpp b/node/InetAddress.cpp
index 7d22eeae..36b4e434 100644
--- a/node/InetAddress.cpp
+++ b/node/InetAddress.cpp
@@ -1,11 +1,11 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * (at your oion) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -32,7 +40,6 @@ const InetAddress InetAddress::LO4((const void *)("\x7f\x00\x00\x01"),4,0);
const InetAddress InetAddress::LO6((const void *)("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"),16,0);
InetAddress::IpScope InetAddress::ipScope() const
- throw()
{
switch(ss_family) {
@@ -55,7 +62,7 @@ InetAddress::IpScope InetAddress::ipScope() const
case 0x37: return IP_SCOPE_PSEUDOPRIVATE; // 55.0.0.0/8 (US DoD)
case 0x38: return IP_SCOPE_PSEUDOPRIVATE; // 56.0.0.0/8 (US Postal Service)
case 0x64:
- if ((ip & 0xffc00000) == 0x64400000) return IP_SCOPE_SHARED; // 100.64.0.0/10
+ if ((ip & 0xffc00000) == 0x64400000) return IP_SCOPE_PRIVATE; // 100.64.0.0/10
break;
case 0x7f: return IP_SCOPE_LOOPBACK; // 127.0.0.0/8
case 0xa9:
@@ -103,126 +110,114 @@ InetAddress::IpScope InetAddress::ipScope() const
return IP_SCOPE_NONE;
}
-void InetAddress::set(const std::string &ip,unsigned int port)
- throw()
-{
- memset(this,0,sizeof(InetAddress));
- if (ip.find(':') != std::string::npos) {
- struct sockaddr_in6 *sin6 = reinterpret_cast<struct sockaddr_in6 *>(this);
- ss_family = AF_INET6;
- sin6->sin6_port = Utils::hton((uint16_t)port);
- if (inet_pton(AF_INET6,ip.c_str(),(void *)&(sin6->sin6_addr.s6_addr)) <= 0)
- memset(this,0,sizeof(InetAddress));
- } else if (ip.find('.') != std::string::npos) {
- struct sockaddr_in *sin = reinterpret_cast<struct sockaddr_in *>(this);
- ss_family = AF_INET;
- sin->sin_port = Utils::hton((uint16_t)port);
- if (inet_pton(AF_INET,ip.c_str(),(void *)&(sin->sin_addr.s_addr)) <= 0)
- memset(this,0,sizeof(InetAddress));
- }
-}
-
void InetAddress::set(const void *ipBytes,unsigned int ipLen,unsigned int port)
- throw()
{
memset(this,0,sizeof(InetAddress));
if (ipLen == 4) {
uint32_t ipb[1];
- memcpy(ipb,ipBytes,4);
+ ZT_FAST_MEMCPY(ipb,ipBytes,4);
ss_family = AF_INET;
reinterpret_cast<struct sockaddr_in *>(this)->sin_addr.s_addr = ipb[0];
reinterpret_cast<struct sockaddr_in *>(this)->sin_port = Utils::hton((uint16_t)port);
} else if (ipLen == 16) {
ss_family = AF_INET6;
- memcpy(reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,ipBytes,16);
+ ZT_FAST_MEMCPY(reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,ipBytes,16);
reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_port = Utils::hton((uint16_t)port);
}
}
-std::string InetAddress::toString() const
+char *InetAddress::toString(char buf[64]) const
{
- char buf[128];
- switch(ss_family) {
- case AF_INET:
- Utils::snprintf(buf,sizeof(buf),"%d.%d.%d.%d/%d",
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[0],
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[1],
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[2],
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[3],
- (int)Utils::ntoh((uint16_t)(reinterpret_cast<const struct sockaddr_in *>(this)->sin_port))
- );
- return std::string(buf);
- case AF_INET6:
- Utils::snprintf(buf,sizeof(buf),"%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x/%d",
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[0]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[1]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[2]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[3]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[4]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[5]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[6]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[7]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[8]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[9]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[10]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[11]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[12]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[13]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[14]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[15]),
- (int)Utils::ntoh((uint16_t)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port))
- );
- return std::string(buf);
+ char *p = toIpString(buf);
+ if (*p) {
+ while (*p) ++p;
+ *(p++) = '/';
+ Utils::decimal(port(),p);
}
- return std::string();
+ return buf;
}
-std::string InetAddress::toIpString() const
+char *InetAddress::toIpString(char buf[64]) const
{
- char buf[128];
switch(ss_family) {
- case AF_INET:
- Utils::snprintf(buf,sizeof(buf),"%d.%d.%d.%d",
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[0],
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[1],
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[2],
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[3]
- );
- return std::string(buf);
- case AF_INET6:
- Utils::snprintf(buf,sizeof(buf),"%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x",
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[0]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[1]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[2]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[3]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[4]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[5]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[6]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[7]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[8]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[9]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[10]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[11]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[12]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[13]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[14]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[15])
- );
- return std::string(buf);
+ case AF_INET: {
+ const uint8_t *a = reinterpret_cast<const uint8_t *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr));
+ char *p = buf;
+ for(int i=0;;++i) {
+ Utils::decimal((unsigned long)a[i],p);
+ if (i != 3) {
+ while (*p) ++p;
+ *(p++) = '.';
+ } else break;
+ }
+ } break;
+
+ case AF_INET6: {
+ uint16_t a[8];
+ ZT_FAST_MEMCPY(a,reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,16);
+ char *p = buf;
+ for(int i=0;i<8;++i) {
+ Utils::hex(Utils::ntoh(a[i]),p);
+ p[4] = (i == 7) ? (char)0 : ':';
+ p += 5;
+ }
+ } break;
+
+ default:
+ buf[0] = (char)0;
+ break;
}
- return std::string();
+ return buf;
}
-void InetAddress::fromString(const std::string &ipSlashPort)
+bool InetAddress::fromString(const char *ipSlashPort)
{
- const std::size_t slashAt = ipSlashPort.find('/');
- if (slashAt == std::string::npos) {
- set(ipSlashPort,0);
+ char buf[64];
+
+ memset(this,0,sizeof(InetAddress));
+
+ if (!*ipSlashPort)
+ return true;
+ if (!Utils::scopy(buf,sizeof(buf),ipSlashPort))
+ return false;
+
+ char *portAt = buf;
+ while ((*portAt)&&(*portAt != '/'))
+ ++portAt;
+ unsigned int port = 0;
+ if (*portAt) {
+ *(portAt++) = (char)0;
+ port = Utils::strToUInt(portAt) & 0xffff;
+ }
+
+ if (strchr(buf,':')) {
+ uint16_t a[8];
+ unsigned int b = 0;
+ char *saveptr = (char *)0;
+ for(char *s=Utils::stok(buf,":",&saveptr);((s)&&(b<8));s=Utils::stok((char *)0,":",&saveptr))
+ a[b++] = Utils::hton((uint16_t)(Utils::hexStrToUInt(s) & 0xffff));
+
+ struct sockaddr_in6 *const in6 = reinterpret_cast<struct sockaddr_in6 *>(this);
+ in6->sin6_family = AF_INET6;
+ ZT_FAST_MEMCPY(in6->sin6_addr.s6_addr,a,16);
+ in6->sin6_port = Utils::hton((uint16_t)port);
+
+ return true;
+ } else if (strchr(buf,'.')) {
+ uint8_t a[4];
+ unsigned int b = 0;
+ char *saveptr = (char *)0;
+ for(char *s=Utils::stok(buf,".",&saveptr);((s)&&(b<4));s=Utils::stok((char *)0,".",&saveptr))
+ a[b++] = (uint8_t)(Utils::strToUInt(s) & 0xff);
+
+ struct sockaddr_in *const in = reinterpret_cast<struct sockaddr_in *>(this);
+ in->sin_family = AF_INET;
+ ZT_FAST_MEMCPY(&(in->sin_addr.s_addr),a,4);
+ in->sin_port = Utils::hton((uint16_t)port);
+
+ return true;
} else {
- long p = strtol(ipSlashPort.substr(slashAt+1).c_str(),(char **)0,10);
- if ((p > 0)&&(p <= 0xffff))
- set(ipSlashPort.substr(0,slashAt),(unsigned int)p);
- else set(ipSlashPort.substr(0,slashAt),0);
+ return false;
}
}
@@ -236,15 +231,14 @@ InetAddress InetAddress::netmask() const
case AF_INET6: {
uint64_t nm[2];
const unsigned int bits = netmaskBits();
- if(bits) {
- nm[0] = Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits))));
- nm[1] = Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits))));
- }
- else {
- nm[0] = 0;
- nm[1] = 0;
- }
- memcpy(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,nm,16);
+ if(bits) {
+ nm[0] = Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits))));
+ nm[1] = Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits))));
+ } else {
+ nm[0] = 0;
+ nm[1] = 0;
+ }
+ ZT_FAST_MEMCPY(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,nm,16);
} break;
}
return r;
@@ -270,15 +264,37 @@ InetAddress InetAddress::network() const
case AF_INET6: {
uint64_t nm[2];
const unsigned int bits = netmaskBits();
- memcpy(nm,reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,16);
+ ZT_FAST_MEMCPY(nm,reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,16);
nm[0] &= Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits))));
nm[1] &= Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits))));
- memcpy(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,nm,16);
+ ZT_FAST_MEMCPY(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,nm,16);
} break;
}
return r;
}
+bool InetAddress::isEqualPrefix(const InetAddress &addr) const
+{
+ if (addr.ss_family == ss_family) {
+ switch(ss_family) {
+ case AF_INET6: {
+ const InetAddress mask(netmask());
+ InetAddress addr_mask(addr.netmask());
+ const uint8_t *n = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&addr_mask)->sin6_addr.s6_addr);
+ const uint8_t *m = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&mask)->sin6_addr.s6_addr);
+ const uint8_t *a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_addr.s6_addr);
+ const uint8_t *b = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
+ for(unsigned int i=0;i<16;++i) {
+ if ((a[i] & m[i]) != (b[i] & n[i]))
+ return false;
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
bool InetAddress::containsAddress(const InetAddress &addr) const
{
if (addr.ss_family == ss_family) {
@@ -306,7 +322,6 @@ bool InetAddress::containsAddress(const InetAddress &addr) const
}
bool InetAddress::isNetwork() const
- throw()
{
switch(ss_family) {
case AF_INET: {
@@ -339,7 +354,6 @@ bool InetAddress::isNetwork() const
}
bool InetAddress::operator==(const InetAddress &a) const
- throw()
{
if (ss_family == a.ss_family) {
switch(ss_family) {
@@ -363,7 +377,6 @@ bool InetAddress::operator==(const InetAddress &a) const
}
bool InetAddress::operator<(const InetAddress &a) const
- throw()
{
if (ss_family < a.ss_family)
return true;
diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp
index c37fa621..f69b3cc2 100644
--- a/node/InetAddress.hpp
+++ b/node/InetAddress.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_INETADDRESS_HPP
@@ -23,8 +31,6 @@
#include <string.h>
#include <stdint.h>
-#include <string>
-
#include "Constants.hpp"
#include "../include/ZeroTierOne.h"
#include "Utils.hpp"
@@ -73,110 +79,106 @@ struct InetAddress : public sockaddr_storage
IP_SCOPE_PSEUDOPRIVATE = 3, // 28.x.x.x, etc. -- unofficially unrouted IPv4 blocks often "bogarted"
IP_SCOPE_GLOBAL = 4, // globally routable IP address (all others)
IP_SCOPE_LINK_LOCAL = 5, // 169.254.x.x, IPv6 LL
- IP_SCOPE_SHARED = 6, // 100.64.0.0/10, shared space for e.g. carrier-grade NAT
+ IP_SCOPE_SHARED = 6, // currently unused, formerly used for carrier-grade NAT ranges
IP_SCOPE_PRIVATE = 7 // 10.x.x.x, 192.168.x.x, etc.
};
- InetAddress() throw() { memset(this,0,sizeof(InetAddress)); }
- InetAddress(const InetAddress &a) throw() { memcpy(this,&a,sizeof(InetAddress)); }
- InetAddress(const InetAddress *a) throw() { memcpy(this,a,sizeof(InetAddress)); }
- InetAddress(const struct sockaddr_storage &ss) throw() { *this = ss; }
- InetAddress(const struct sockaddr_storage *ss) throw() { *this = ss; }
- InetAddress(const struct sockaddr &sa) throw() { *this = sa; }
- InetAddress(const struct sockaddr *sa) throw() { *this = sa; }
- InetAddress(const struct sockaddr_in &sa) throw() { *this = sa; }
- InetAddress(const struct sockaddr_in *sa) throw() { *this = sa; }
- InetAddress(const struct sockaddr_in6 &sa) throw() { *this = sa; }
- InetAddress(const struct sockaddr_in6 *sa) throw() { *this = sa; }
- InetAddress(const void *ipBytes,unsigned int ipLen,unsigned int port) throw() { this->set(ipBytes,ipLen,port); }
- InetAddress(const uint32_t ipv4,unsigned int port) throw() { this->set(&ipv4,4,port); }
- InetAddress(const std::string &ip,unsigned int port) throw() { this->set(ip,port); }
- InetAddress(const std::string &ipSlashPort) throw() { this->fromString(ipSlashPort); }
- InetAddress(const char *ipSlashPort) throw() { this->fromString(std::string(ipSlashPort)); }
+ // Can be used with the unordered maps and sets in c++11. We don't use C++11 in the core
+ // but this is safe to put here.
+ struct Hasher
+ {
+ inline std::size_t operator()(const InetAddress &a) const { return (std::size_t)a.hashCode(); }
+ };
+
+ InetAddress() { memset(this,0,sizeof(InetAddress)); }
+ InetAddress(const InetAddress &a) { ZT_FAST_MEMCPY(this,&a,sizeof(InetAddress)); }
+ InetAddress(const InetAddress *a) { ZT_FAST_MEMCPY(this,a,sizeof(InetAddress)); }
+ InetAddress(const struct sockaddr_storage &ss) { *this = ss; }
+ InetAddress(const struct sockaddr_storage *ss) { *this = ss; }
+ InetAddress(const struct sockaddr &sa) { *this = sa; }
+ InetAddress(const struct sockaddr *sa) { *this = sa; }
+ InetAddress(const struct sockaddr_in &sa) { *this = sa; }
+ InetAddress(const struct sockaddr_in *sa) { *this = sa; }
+ InetAddress(const struct sockaddr_in6 &sa) { *this = sa; }
+ InetAddress(const struct sockaddr_in6 *sa) { *this = sa; }
+ InetAddress(const void *ipBytes,unsigned int ipLen,unsigned int port) { this->set(ipBytes,ipLen,port); }
+ InetAddress(const uint32_t ipv4,unsigned int port) { this->set(&ipv4,4,port); }
+ InetAddress(const char *ipSlashPort) { this->fromString(ipSlashPort); }
inline InetAddress &operator=(const InetAddress &a)
- throw()
{
if (&a != this)
- memcpy(this,&a,sizeof(InetAddress));
+ ZT_FAST_MEMCPY(this,&a,sizeof(InetAddress));
return *this;
}
inline InetAddress &operator=(const InetAddress *a)
- throw()
{
if (a != this)
- memcpy(this,a,sizeof(InetAddress));
+ ZT_FAST_MEMCPY(this,a,sizeof(InetAddress));
return *this;
}
inline InetAddress &operator=(const struct sockaddr_storage &ss)
- throw()
{
if (reinterpret_cast<const InetAddress *>(&ss) != this)
- memcpy(this,&ss,sizeof(InetAddress));
+ ZT_FAST_MEMCPY(this,&ss,sizeof(InetAddress));
return *this;
}
inline InetAddress &operator=(const struct sockaddr_storage *ss)
- throw()
{
if (reinterpret_cast<const InetAddress *>(ss) != this)
- memcpy(this,ss,sizeof(InetAddress));
+ ZT_FAST_MEMCPY(this,ss,sizeof(InetAddress));
return *this;
}
inline InetAddress &operator=(const struct sockaddr_in &sa)
- throw()
{
if (reinterpret_cast<const InetAddress *>(&sa) != this) {
memset(this,0,sizeof(InetAddress));
- memcpy(this,&sa,sizeof(struct sockaddr_in));
+ ZT_FAST_MEMCPY(this,&sa,sizeof(struct sockaddr_in));
}
return *this;
}
inline InetAddress &operator=(const struct sockaddr_in *sa)
- throw()
{
if (reinterpret_cast<const InetAddress *>(sa) != this) {
memset(this,0,sizeof(InetAddress));
- memcpy(this,sa,sizeof(struct sockaddr_in));
+ ZT_FAST_MEMCPY(this,sa,sizeof(struct sockaddr_in));
}
return *this;
}
inline InetAddress &operator=(const struct sockaddr_in6 &sa)
- throw()
{
if (reinterpret_cast<const InetAddress *>(&sa) != this) {
memset(this,0,sizeof(InetAddress));
- memcpy(this,&sa,sizeof(struct sockaddr_in6));
+ ZT_FAST_MEMCPY(this,&sa,sizeof(struct sockaddr_in6));
}
return *this;
}
inline InetAddress &operator=(const struct sockaddr_in6 *sa)
- throw()
{
if (reinterpret_cast<const InetAddress *>(sa) != this) {
memset(this,0,sizeof(InetAddress));
- memcpy(this,sa,sizeof(struct sockaddr_in6));
+ ZT_FAST_MEMCPY(this,sa,sizeof(struct sockaddr_in6));
}
return *this;
}
inline InetAddress &operator=(const struct sockaddr &sa)
- throw()
{
if (reinterpret_cast<const InetAddress *>(&sa) != this) {
memset(this,0,sizeof(InetAddress));
switch(sa.sa_family) {
case AF_INET:
- memcpy(this,&sa,sizeof(struct sockaddr_in));
+ ZT_FAST_MEMCPY(this,&sa,sizeof(struct sockaddr_in));
break;
case AF_INET6:
- memcpy(this,&sa,sizeof(struct sockaddr_in6));
+ ZT_FAST_MEMCPY(this,&sa,sizeof(struct sockaddr_in6));
break;
}
}
@@ -184,16 +186,15 @@ struct InetAddress : public sockaddr_storage
}
inline InetAddress &operator=(const struct sockaddr *sa)
- throw()
{
if (reinterpret_cast<const InetAddress *>(sa) != this) {
memset(this,0,sizeof(InetAddress));
switch(sa->sa_family) {
case AF_INET:
- memcpy(this,sa,sizeof(struct sockaddr_in));
+ ZT_FAST_MEMCPY(this,sa,sizeof(struct sockaddr_in));
break;
case AF_INET6:
- memcpy(this,sa,sizeof(struct sockaddr_in6));
+ ZT_FAST_MEMCPY(this,sa,sizeof(struct sockaddr_in6));
break;
}
}
@@ -203,17 +204,7 @@ struct InetAddress : public sockaddr_storage
/**
* @return IP scope classification (e.g. loopback, link-local, private, global)
*/
- IpScope ipScope() const
- throw();
-
- /**
- * Set from a string-format IP and a port
- *
- * @param ip IP address in V4 or V6 ASCII notation
- * @param port Port or 0 for none
- */
- void set(const std::string &ip,unsigned int port)
- throw();
+ IpScope ipScope() const;
/**
* Set from a raw IP and port number
@@ -222,8 +213,7 @@ struct InetAddress : public sockaddr_storage
* @param ipLen Length of IP address: 4 or 16
* @param port Port number or 0 for none
*/
- void set(const void *ipBytes,unsigned int ipLen,unsigned int port)
- throw();
+ void set(const void *ipBytes,unsigned int ipLen,unsigned int port);
/**
* Set the port component
@@ -264,23 +254,23 @@ struct InetAddress : public sockaddr_storage
/**
* @return ASCII IP/port format representation
*/
- std::string toString() const;
+ char *toString(char buf[64]) const;
/**
* @return IP portion only, in ASCII string format
*/
- std::string toIpString() const;
+ char *toIpString(char buf[64]) const;
/**
- * @param ipSlashPort ASCII IP/port format notation
+ * @param ipSlashPort IP/port (port is optional, will be 0 if not included)
+ * @return True if address appeared to be valid
*/
- void fromString(const std::string &ipSlashPort);
+ bool fromString(const char *ipSlashPort);
/**
* @return Port or 0 if no port component defined
*/
inline unsigned int port() const
- throw()
{
switch(ss_family) {
case AF_INET: return Utils::ntoh((uint16_t)(reinterpret_cast<const struct sockaddr_in *>(this)->sin_port));
@@ -298,7 +288,7 @@ struct InetAddress : public sockaddr_storage
*
* @return Netmask bits
*/
- inline unsigned int netmaskBits() const throw() { return port(); }
+ inline unsigned int netmaskBits() const { return port(); }
/**
* @return True if netmask bits is valid for the address type
@@ -321,7 +311,7 @@ struct InetAddress : public sockaddr_storage
*
* @return Gateway metric
*/
- inline unsigned int metric() const throw() { return port(); }
+ inline unsigned int metric() const { return port(); }
/**
* Construct a full netmask as an InetAddress
@@ -348,6 +338,14 @@ struct InetAddress : public sockaddr_storage
InetAddress network() const;
/**
+ * Test whether this IPv6 prefix matches the prefix of a given IPv6 address
+ *
+ * @param addr Address to check
+ * @return True if this IPv6 prefix matches the prefix of a given IPv6 address
+ */
+ bool isEqualPrefix(const InetAddress &addr) const;
+
+ /**
* Test whether this IP/netmask contains this address
*
* @param addr Address to check
@@ -358,12 +356,12 @@ struct InetAddress : public sockaddr_storage
/**
* @return True if this is an IPv4 address
*/
- inline bool isV4() const throw() { return (ss_family == AF_INET); }
+ inline bool isV4() const { return (ss_family == AF_INET); }
/**
* @return True if this is an IPv6 address
*/
- inline bool isV6() const throw() { return (ss_family == AF_INET6); }
+ inline bool isV6() const { return (ss_family == AF_INET6); }
/**
* @return pointer to raw address bytes or NULL if not available
@@ -390,7 +388,7 @@ struct InetAddress : public sockaddr_storage
break;
case AF_INET6:
r.ss_family = AF_INET6;
- memcpy(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,16);
+ ZT_FAST_MEMCPY(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,16);
break;
}
return r;
@@ -414,6 +412,26 @@ struct InetAddress : public sockaddr_storage
return false;
}
+ /**
+ * Performs an IP-only comparison or, if that is impossible, a memcmp()
+ *
+ * This version compares only the first 64 bits of IPv6 addresses.
+ *
+ * @param a InetAddress to compare again
+ * @return True if only IP portions are equal (false for non-IP or null addresses)
+ */
+ inline bool ipsEqual2(const InetAddress &a) const
+ {
+ if (ss_family == a.ss_family) {
+ if (ss_family == AF_INET)
+ return (reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr == reinterpret_cast<const struct sockaddr_in *>(&a)->sin_addr.s_addr);
+ if (ss_family == AF_INET6)
+ return (memcmp(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_addr.s6_addr,8) == 0);
+ return (memcmp(this,&a,sizeof(InetAddress)) == 0);
+ }
+ return false;
+ }
+
inline unsigned long hashCode() const
{
if (ss_family == AF_INET) {
@@ -436,7 +454,7 @@ struct InetAddress : public sockaddr_storage
/**
* Set to null/zero
*/
- inline void zero() throw() { memset(this,0,sizeof(InetAddress)); }
+ inline void zero() { memset(this,0,sizeof(InetAddress)); }
/**
* Check whether this is a network/route rather than an IP assignment
@@ -446,8 +464,7 @@ struct InetAddress : public sockaddr_storage
*
* @return True if everything after netmask bits is zero
*/
- bool isNetwork() const
- throw();
+ bool isNetwork() const;
/**
* @return 14-bit (0-16383) hash of this IP's first 24 or 48 bits (for V4 or V6) for rate limiting code, or 0 if non-IP
@@ -476,7 +493,7 @@ struct InetAddress : public sockaddr_storage
/**
* @return True if address family is non-zero
*/
- inline operator bool() const throw() { return (ss_family != 0); }
+ inline operator bool() const { return (ss_family != 0); }
template<unsigned int C>
inline void serialize(Buffer<C> &b) const
@@ -520,26 +537,26 @@ struct InetAddress : public sockaddr_storage
return (unsigned int)(b.template at<uint16_t>(p) + 3); // other addresses begin with 16-bit non-inclusive length
case 0x04:
ss_family = AF_INET;
- memcpy(&(reinterpret_cast<struct sockaddr_in *>(this)->sin_addr.s_addr),b.field(p,4),4); p += 4;
+ ZT_FAST_MEMCPY(&(reinterpret_cast<struct sockaddr_in *>(this)->sin_addr.s_addr),b.field(p,4),4); p += 4;
reinterpret_cast<struct sockaddr_in *>(this)->sin_port = Utils::hton(b.template at<uint16_t>(p)); p += 2;
break;
case 0x06:
ss_family = AF_INET6;
- memcpy(reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,b.field(p,16),16); p += 16;
+ ZT_FAST_MEMCPY(reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,b.field(p,16),16); p += 16;
reinterpret_cast<struct sockaddr_in *>(this)->sin_port = Utils::hton(b.template at<uint16_t>(p)); p += 2;
break;
default:
- throw std::invalid_argument("invalid serialized InetAddress");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_BAD_ENCODING;
}
return (p - startAt);
}
- bool operator==(const InetAddress &a) const throw();
- bool operator<(const InetAddress &a) const throw();
- inline bool operator!=(const InetAddress &a) const throw() { return !(*this == a); }
- inline bool operator>(const InetAddress &a) const throw() { return (a < *this); }
- inline bool operator<=(const InetAddress &a) const throw() { return !(a < *this); }
- inline bool operator>=(const InetAddress &a) const throw() { return !(*this < a); }
+ bool operator==(const InetAddress &a) const;
+ bool operator<(const InetAddress &a) const;
+ inline bool operator!=(const InetAddress &a) const { return !(*this == a); }
+ inline bool operator>(const InetAddress &a) const { return (a < *this); }
+ inline bool operator<=(const InetAddress &a) const { return !(a < *this); }
+ inline bool operator>=(const InetAddress &a) const { return !(*this < a); }
/**
* @param mac MAC address seed
diff --git a/node/MAC.hpp b/node/MAC.hpp
index 95623f12..c08323a4 100644
--- a/node/MAC.hpp
+++ b/node/MAC.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_MAC_HPP
@@ -36,30 +44,24 @@ namespace ZeroTier {
class MAC
{
public:
- MAC() throw() : _m(0ULL) {}
- MAC(const MAC &m) throw() : _m(m._m) {}
+ MAC() : _m(0ULL) {}
+ MAC(const MAC &m) : _m(m._m) {}
- MAC(const unsigned char a,const unsigned char b,const unsigned char c,const unsigned char d,const unsigned char e,const unsigned char f) throw() :
+ MAC(const unsigned char a,const unsigned char b,const unsigned char c,const unsigned char d,const unsigned char e,const unsigned char f) :
_m( ((((uint64_t)a) & 0xffULL) << 40) |
((((uint64_t)b) & 0xffULL) << 32) |
((((uint64_t)c) & 0xffULL) << 24) |
((((uint64_t)d) & 0xffULL) << 16) |
((((uint64_t)e) & 0xffULL) << 8) |
(((uint64_t)f) & 0xffULL) ) {}
-
- MAC(const char *s) throw() { fromString(s); }
- MAC(const std::string &s) throw() { fromString(s.c_str()); }
-
- MAC(const void *bits,unsigned int len) throw() { setTo(bits,len); }
-
- MAC(const Address &ztaddr,uint64_t nwid) throw() { fromAddress(ztaddr,nwid); }
-
- MAC(const uint64_t m) throw() : _m(m & 0xffffffffffffULL) {}
+ MAC(const void *bits,unsigned int len) { setTo(bits,len); }
+ MAC(const Address &ztaddr,uint64_t nwid) { fromAddress(ztaddr,nwid); }
+ MAC(const uint64_t m) : _m(m & 0xffffffffffffULL) {}
/**
* @return MAC in 64-bit integer
*/
- inline uint64_t toInt() const throw() { return _m; }
+ inline uint64_t toInt() const { return _m; }
/**
* Set MAC to zero
@@ -69,14 +71,13 @@ public:
/**
* @return True if MAC is non-zero
*/
- inline operator bool() const throw() { return (_m != 0ULL); }
+ inline operator bool() const { return (_m != 0ULL); }
/**
* @param bits Raw MAC in big-endian byte order
* @param len Length, must be >= 6 or result is zero
*/
inline void setTo(const void *bits,unsigned int len)
- throw()
{
if (len < 6) {
_m = 0ULL;
@@ -96,7 +97,6 @@ public:
* @param len Length of buffer, must be >= 6 or nothing is copied
*/
inline void copyTo(void *buf,unsigned int len) const
- throw()
{
if (len < 6)
return;
@@ -116,7 +116,6 @@ public:
*/
template<unsigned int C>
inline void appendTo(Buffer<C> &b) const
- throw(std::out_of_range)
{
unsigned char *p = (unsigned char *)b.appendField(6);
*(p++) = (unsigned char)((_m >> 40) & 0xff);
@@ -130,48 +129,17 @@ public:
/**
* @return True if this is broadcast (all 0xff)
*/
- inline bool isBroadcast() const throw() { return (_m == 0xffffffffffffULL); }
+ inline bool isBroadcast() const { return (_m == 0xffffffffffffULL); }
/**
* @return True if this is a multicast MAC
*/
- inline bool isMulticast() const throw() { return ((_m & 0x010000000000ULL) != 0ULL); }
+ inline bool isMulticast() const { return ((_m & 0x010000000000ULL) != 0ULL); }
/**
* @param True if this is a locally-administered MAC
*/
- inline bool isLocallyAdministered() const throw() { return ((_m & 0x020000000000ULL) != 0ULL); }
-
- /**
- * @param s Hex MAC, with or without : delimiters
- */
- inline void fromString(const char *s)
- {
- char tmp[8];
- for(int i=0;i<6;++i)
- tmp[i] = (char)0;
- Utils::unhex(s,tmp,6);
- setTo(tmp,6);
- }
-
- /**
- * @return MAC address in standard :-delimited hex format
- */
- inline std::string toString() const
- {
- char tmp[24];
- toString(tmp,sizeof(tmp));
- return std::string(tmp);
- }
-
- /**
- * @param buf Buffer to contain human-readable MAC
- * @param len Length of buffer
- */
- inline void toString(char *buf,unsigned int len) const
- {
- Utils::snprintf(buf,len,"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)(*this)[0],(int)(*this)[1],(int)(*this)[2],(int)(*this)[3],(int)(*this)[4],(int)(*this)[5]);
- }
+ inline bool isLocallyAdministered() const { return ((_m & 0x020000000000ULL) != 0ULL); }
/**
* Set this MAC to a MAC derived from an address and a network ID
@@ -180,7 +148,6 @@ public:
* @param nwid 64-bit network ID
*/
inline void fromAddress(const Address &ztaddr,uint64_t nwid)
- throw()
{
uint64_t m = ((uint64_t)firstOctetForNetwork(nwid)) << 40;
m |= ztaddr.toInt(); // a is 40 bits
@@ -200,7 +167,6 @@ public:
* @param nwid Network ID
*/
inline Address toAddress(uint64_t nwid) const
- throw()
{
uint64_t a = _m & 0xffffffffffULL; // least significant 40 bits of MAC are formed from address
a ^= ((nwid >> 8) & 0xff) << 32; // ... XORed with bits 8-48 of the nwid in little-endian byte order, so unmask it
@@ -216,7 +182,6 @@ public:
* @return First octet of MAC for this network
*/
static inline unsigned char firstOctetForNetwork(uint64_t nwid)
- throw()
{
unsigned char a = ((unsigned char)(nwid & 0xfe) | 0x02); // locally administered, not multicast, from LSB of network ID
return ((a == 0x52) ? 0x32 : a); // blacklist 0x52 since it's used by KVM, libvirt, and other popular virtualization engines... seems de-facto standard on Linux
@@ -226,34 +191,55 @@ public:
* @param i Value from 0 to 5 (inclusive)
* @return Byte at said position (address interpreted in big-endian order)
*/
- inline unsigned char operator[](unsigned int i) const throw() { return (unsigned char)((_m >> (40 - (i * 8))) & 0xff); }
+ inline unsigned char operator[](unsigned int i) const { return (unsigned char)((_m >> (40 - (i * 8))) & 0xff); }
/**
* @return 6, which is the number of bytes in a MAC, for container compliance
*/
- inline unsigned int size() const throw() { return 6; }
+ inline unsigned int size() const { return 6; }
- inline unsigned long hashCode() const throw() { return (unsigned long)_m; }
+ inline unsigned long hashCode() const { return (unsigned long)_m; }
+
+ inline char *toString(char buf[18]) const
+ {
+ buf[0] = Utils::HEXCHARS[(_m >> 44) & 0xf];
+ buf[1] = Utils::HEXCHARS[(_m >> 40) & 0xf];
+ buf[2] = ':';
+ buf[3] = Utils::HEXCHARS[(_m >> 36) & 0xf];
+ buf[4] = Utils::HEXCHARS[(_m >> 32) & 0xf];
+ buf[5] = ':';
+ buf[6] = Utils::HEXCHARS[(_m >> 28) & 0xf];
+ buf[7] = Utils::HEXCHARS[(_m >> 24) & 0xf];
+ buf[8] = ':';
+ buf[9] = Utils::HEXCHARS[(_m >> 20) & 0xf];
+ buf[10] = Utils::HEXCHARS[(_m >> 16) & 0xf];
+ buf[11] = ':';
+ buf[12] = Utils::HEXCHARS[(_m >> 12) & 0xf];
+ buf[13] = Utils::HEXCHARS[(_m >> 8) & 0xf];
+ buf[14] = ':';
+ buf[15] = Utils::HEXCHARS[(_m >> 4) & 0xf];
+ buf[16] = Utils::HEXCHARS[_m & 0xf];
+ buf[17] = (char)0;
+ return buf;
+ }
inline MAC &operator=(const MAC &m)
- throw()
{
_m = m._m;
return *this;
}
inline MAC &operator=(const uint64_t m)
- throw()
{
_m = m;
return *this;
}
- inline bool operator==(const MAC &m) const throw() { return (_m == m._m); }
- inline bool operator!=(const MAC &m) const throw() { return (_m != m._m); }
- inline bool operator<(const MAC &m) const throw() { return (_m < m._m); }
- inline bool operator<=(const MAC &m) const throw() { return (_m <= m._m); }
- inline bool operator>(const MAC &m) const throw() { return (_m > m._m); }
- inline bool operator>=(const MAC &m) const throw() { return (_m >= m._m); }
+ inline bool operator==(const MAC &m) const { return (_m == m._m); }
+ inline bool operator!=(const MAC &m) const { return (_m != m._m); }
+ inline bool operator<(const MAC &m) const { return (_m < m._m); }
+ inline bool operator<=(const MAC &m) const { return (_m <= m._m); }
+ inline bool operator>(const MAC &m) const { return (_m > m._m); }
+ inline bool operator>=(const MAC &m) const { return (_m >= m._m); }
private:
uint64_t _m;
diff --git a/node/Membership.cpp b/node/Membership.cpp
index 2d0471f1..affe7a71 100644
--- a/node/Membership.cpp
+++ b/node/Membership.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <algorithm>
@@ -25,6 +33,7 @@
#include "Switch.hpp"
#include "Packet.hpp"
#include "Node.hpp"
+#include "Trace.hpp"
#define ZT_CREDENTIAL_PUSH_EVERY (ZT_NETWORK_AUTOCONF_DELAY / 3)
@@ -42,7 +51,7 @@ Membership::Membership() :
resetPushState();
}
-void Membership::pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const uint64_t now,const Address &peerAddress,const NetworkConfig &nconf,int localCapabilityIndex,const bool force)
+void Membership::pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const int64_t now,const Address &peerAddress,const NetworkConfig &nconf,int localCapabilityIndex,const bool force)
{
bool sendCom = ( (nconf.com) && ( ((now - _lastPushedCom) >= ZT_CREDENTIAL_PUSH_EVERY) || (force) ) );
@@ -118,28 +127,25 @@ void Membership::pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const u
Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfMembership &com)
{
- const uint64_t newts = com.timestamp();
+ const int64_t newts = com.timestamp();
if (newts <= _comRevocationThreshold) {
- TRACE("addCredential(CertificateOfMembership) for %s on %.16llx REJECTED (revoked)",com.issuedTo().toString().c_str(),com.networkId());
+ RR->t->credentialRejected(tPtr,com,"revoked");
return ADD_REJECTED;
}
- const uint64_t oldts = _com.timestamp();
+ const int64_t oldts = _com.timestamp();
if (newts < oldts) {
- TRACE("addCredential(CertificateOfMembership) for %s on %.16llx REJECTED (older than current)",com.issuedTo().toString().c_str(),com.networkId());
+ RR->t->credentialRejected(tPtr,com,"old");
return ADD_REJECTED;
}
- if ((newts == oldts)&&(_com == com)) {
- TRACE("addCredential(CertificateOfMembership) for %s on %.16llx ACCEPTED (redundant)",com.issuedTo().toString().c_str(),com.networkId());
+ if ((newts == oldts)&&(_com == com))
return ADD_ACCEPTED_REDUNDANT;
- }
switch(com.verify(RR,tPtr)) {
default:
- TRACE("addCredential(CertificateOfMembership) for %s on %.16llx REJECTED (invalid signature or object)",com.issuedTo().toString().c_str(),com.networkId());
+ RR->t->credentialRejected(tPtr,com,"invalid");
return ADD_REJECTED;
case 0:
- TRACE("addCredential(CertificateOfMembership) for %s on %.16llx ACCEPTED (new)",com.issuedTo().toString().c_str(),com.networkId());
_com = com;
return ADD_ACCEPTED_NEW;
case 1:
@@ -149,32 +155,29 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
// Template out addCredential() for many cred types to avoid copypasta
template<typename C>
-static Membership::AddCredentialResult _addCredImpl(Hashtable<uint32_t,C> &remoteCreds,const Hashtable<uint64_t,uint64_t> &revocations,const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const C &cred)
+static Membership::AddCredentialResult _addCredImpl(Hashtable<uint32_t,C> &remoteCreds,const Hashtable<uint64_t,int64_t> &revocations,const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const C &cred)
{
C *rc = remoteCreds.get(cred.id());
if (rc) {
if (rc->timestamp() > cred.timestamp()) {
- TRACE("addCredential(type==%d) for %s on %.16llx REJECTED (older than credential we have)",(int)C::credentialType(),cred.issuedTo().toString().c_str(),cred.networkId());
+ RR->t->credentialRejected(tPtr,cred,"old");
return Membership::ADD_REJECTED;
}
- if (*rc == cred) {
- //TRACE("addCredential(type==%d) for %s on %.16llx ACCEPTED (redundant)",(int)C::credentialType(),cred.issuedTo().toString().c_str(),cred.networkId());
+ if (*rc == cred)
return Membership::ADD_ACCEPTED_REDUNDANT;
- }
}
- const uint64_t *const rt = revocations.get(Membership::credentialKey(C::credentialType(),cred.id()));
+ const int64_t *const rt = revocations.get(Membership::credentialKey(C::credentialType(),cred.id()));
if ((rt)&&(*rt >= cred.timestamp())) {
- TRACE("addCredential(type==%d) for %s on %.16llx REJECTED (timestamp below revocation threshold)",(int)C::credentialType(),cred.issuedTo().toString().c_str(),cred.networkId());
+ RR->t->credentialRejected(tPtr,cred,"revoked");
return Membership::ADD_REJECTED;
}
switch(cred.verify(RR,tPtr)) {
default:
- TRACE("addCredential(type==%d) for %s on %.16llx REJECTED (invalid)",(int)C::credentialType(),cred.issuedTo().toString().c_str(),cred.networkId());
+ RR->t->credentialRejected(tPtr,cred,"invalid");
return Membership::ADD_REJECTED;
case 0:
- TRACE("addCredential(type==%d) for %s on %.16llx ACCEPTED (new)",(int)C::credentialType(),cred.issuedTo().toString().c_str(),cred.networkId());
if (!rc)
rc = &(remoteCreds[cred.id()]);
*rc = cred;
@@ -190,9 +193,10 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Revocation &rev)
{
- uint64_t *rt;
+ int64_t *rt;
switch(rev.verify(RR,tPtr)) {
default:
+ RR->t->credentialRejected(tPtr,rev,"invalid");
return ADD_REJECTED;
case 0: {
const Credential::Type ct = rev.type();
@@ -209,10 +213,12 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
rt = &(_revocations[credentialKey(ct,rev.credentialId())]);
if (*rt < rev.threshold()) {
*rt = rev.threshold();
+ _comRevocationThreshold = rev.threshold();
return ADD_ACCEPTED_NEW;
}
return ADD_ACCEPTED_REDUNDANT;
default:
+ RR->t->credentialRejected(tPtr,rev,"invalid");
return ADD_REJECTED;
}
}
@@ -221,7 +227,7 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
}
}
-void Membership::clean(const uint64_t now,const NetworkConfig &nconf)
+void Membership::clean(const int64_t now,const NetworkConfig &nconf)
{
_cleanCredImpl<Tag>(nconf,_remoteTags);
_cleanCredImpl<Capability>(nconf,_remoteCaps);
diff --git a/node/Membership.hpp b/node/Membership.hpp
index 0bc8f335..ad0bb73e 100644
--- a/node/Membership.hpp
+++ b/node/Membership.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_MEMBERSHIP_HPP
@@ -72,7 +80,7 @@ public:
* @param localCapabilityIndex Index of local capability to include (in nconf.capabilities[]) or -1 if none
* @param force If true, send objects regardless of last push time
*/
- void pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const uint64_t now,const Address &peerAddress,const NetworkConfig &nconf,int localCapabilityIndex,const bool force);
+ void pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const int64_t now,const Address &peerAddress,const NetworkConfig &nconf,int localCapabilityIndex,const bool force);
/**
* Check whether we should push MULTICAST_LIKEs to this peer, and update last sent time if true
@@ -80,7 +88,7 @@ public:
* @param now Current time
* @return True if we should update multicasts
*/
- inline bool multicastLikeGate(const uint64_t now)
+ inline bool multicastLikeGate(const int64_t now)
{
if ((now - _lastUpdatedMulticast) >= ZT_MULTICAST_ANNOUNCE_PERIOD) {
_lastUpdatedMulticast = now;
@@ -102,6 +110,11 @@ public:
return nconf.com.agreesWith(_com);
}
+ inline bool recentlyAssociated(const int64_t now) const
+ {
+ return ((_com)&&((now - _com.timestamp()) < ZT_PEER_ACTIVITY_TIMEOUT));
+ }
+
/**
* Check whether the peer represented by this Membership owns a given resource
*
@@ -167,7 +180,7 @@ public:
* @param now Current time
* @param nconf Current network configuration
*/
- void clean(const uint64_t now,const NetworkConfig &nconf);
+ void clean(const int64_t now,const NetworkConfig &nconf);
/**
* Reset last pushed time for local credentials
@@ -189,9 +202,9 @@ private:
template<typename C>
inline bool _isCredentialTimestampValid(const NetworkConfig &nconf,const C &remoteCredential) const
{
- const uint64_t ts = remoteCredential.timestamp();
+ const int64_t ts = remoteCredential.timestamp();
if (((ts >= nconf.timestamp) ? (ts - nconf.timestamp) : (nconf.timestamp - ts)) <= nconf.credentialTimeMaxDelta) {
- const uint64_t *threshold = _revocations.get(credentialKey(C::credentialType(),remoteCredential.id()));
+ const int64_t *threshold = _revocations.get(credentialKey(C::credentialType(),remoteCredential.id()));
return ((!threshold)||(ts > *threshold));
}
return false;
@@ -210,19 +223,19 @@ private:
}
// Last time we pushed MULTICAST_LIKE(s)
- uint64_t _lastUpdatedMulticast;
+ int64_t _lastUpdatedMulticast;
// Last time we pushed our COM to this peer
- uint64_t _lastPushedCom;
+ int64_t _lastPushedCom;
// Revocation threshold for COM or 0 if none
- uint64_t _comRevocationThreshold;
+ int64_t _comRevocationThreshold;
// Remote member's latest network COM
CertificateOfMembership _com;
// Revocations by credentialKey()
- Hashtable< uint64_t,uint64_t > _revocations;
+ Hashtable< uint64_t,int64_t > _revocations;
// Remote credentials that we have received from this member (and that are valid)
Hashtable< uint32_t,Tag > _remoteTags;
diff --git a/node/MulticastGroup.hpp b/node/MulticastGroup.hpp
index be4e8084..0f4a621e 100644
--- a/node/MulticastGroup.hpp
+++ b/node/MulticastGroup.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_MULTICASTGROUP_HPP
@@ -21,8 +29,6 @@
#include <stdint.h>
-#include <string>
-
#include "MAC.hpp"
#include "InetAddress.hpp"
@@ -46,15 +52,13 @@ namespace ZeroTier {
class MulticastGroup
{
public:
- MulticastGroup()
- throw() :
+ MulticastGroup() :
_mac(),
_adi(0)
{
}
- MulticastGroup(const MAC &m,uint32_t a)
- throw() :
+ MulticastGroup(const MAC &m,uint32_t a) :
_mac(m),
_adi(a)
{
@@ -67,7 +71,6 @@ public:
* @return Multicat group for ARP/NDP
*/
static inline MulticastGroup deriveMulticastGroupForAddressResolution(const InetAddress &ip)
- throw()
{
if (ip.isV4()) {
// IPv4 wants broadcast MACs, so we shove the V4 address itself into
@@ -87,30 +90,20 @@ public:
}
/**
- * @return Human readable string representing this group (MAC/ADI in hex)
- */
- inline std::string toString() const
- {
- char buf[64];
- Utils::snprintf(buf,sizeof(buf),"%.2x%.2x%.2x%.2x%.2x%.2x/%.8lx",(unsigned int)_mac[0],(unsigned int)_mac[1],(unsigned int)_mac[2],(unsigned int)_mac[3],(unsigned int)_mac[4],(unsigned int)_mac[5],(unsigned long)_adi);
- return std::string(buf);
- }
-
- /**
* @return Multicast address
*/
- inline const MAC &mac() const throw() { return _mac; }
+ inline const MAC &mac() const { return _mac; }
/**
* @return Additional distinguishing information
*/
- inline uint32_t adi() const throw() { return _adi; }
+ inline uint32_t adi() const { return _adi; }
- inline unsigned long hashCode() const throw() { return (_mac.hashCode() ^ (unsigned long)_adi); }
+ inline unsigned long hashCode() const { return (_mac.hashCode() ^ (unsigned long)_adi); }
- inline bool operator==(const MulticastGroup &g) const throw() { return ((_mac == g._mac)&&(_adi == g._adi)); }
- inline bool operator!=(const MulticastGroup &g) const throw() { return ((_mac != g._mac)||(_adi != g._adi)); }
- inline bool operator<(const MulticastGroup &g) const throw()
+ inline bool operator==(const MulticastGroup &g) const { return ((_mac == g._mac)&&(_adi == g._adi)); }
+ inline bool operator!=(const MulticastGroup &g) const { return ((_mac != g._mac)||(_adi != g._adi)); }
+ inline bool operator<(const MulticastGroup &g) const
{
if (_mac < g._mac)
return true;
@@ -118,9 +111,9 @@ public:
return (_adi < g._adi);
return false;
}
- inline bool operator>(const MulticastGroup &g) const throw() { return (g < *this); }
- inline bool operator<=(const MulticastGroup &g) const throw() { return !(g < *this); }
- inline bool operator>=(const MulticastGroup &g) const throw() { return !(*this < g); }
+ inline bool operator>(const MulticastGroup &g) const { return (g < *this); }
+ inline bool operator<=(const MulticastGroup &g) const { return !(g < *this); }
+ inline bool operator>=(const MulticastGroup &g) const { return !(*this < g); }
private:
MAC _mac;
diff --git a/node/Multicaster.cpp b/node/Multicaster.cpp
index 8e534b5e..753e4ee0 100644
--- a/node/Multicaster.cpp
+++ b/node/Multicaster.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,13 +14,20 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <algorithm>
#include "Constants.hpp"
#include "RuntimeEnvironment.hpp"
-#include "SharedPtr.hpp"
#include "Multicaster.hpp"
#include "Topology.hpp"
#include "Switch.hpp"
@@ -29,6 +36,7 @@
#include "C25519.hpp"
#include "CertificateOfMembership.hpp"
#include "Node.hpp"
+#include "Network.hpp"
namespace ZeroTier {
@@ -43,7 +51,7 @@ Multicaster::~Multicaster()
{
}
-void Multicaster::addMultiple(void *tPtr,uint64_t now,uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,unsigned int totalKnown)
+void Multicaster::addMultiple(void *tPtr,int64_t now,uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,unsigned int totalKnown)
{
const unsigned char *p = (const unsigned char *)addresses;
const unsigned char *e = p + (5 * count);
@@ -103,7 +111,7 @@ unsigned int Multicaster::gather(const Address &queryingPeer,uint64_t nwid,const
// Members are returned in random order so that repeated gather queries
// will return different subsets of a large multicast group.
k = 0;
- while ((added < limit)&&(k < s->members.size())&&((appendTo.size() + ZT_ADDRESS_LENGTH) <= ZT_UDP_DEFAULT_PAYLOAD_MTU)) {
+ while ((added < limit)&&(k < s->members.size())&&((appendTo.size() + ZT_ADDRESS_LENGTH) <= ZT_PROTO_MAX_PACKET_LENGTH)) {
rptr = (unsigned int)RR->node->prng();
restart_member_scan:
@@ -131,8 +139,6 @@ restart_member_scan:
appendTo.setAt(totalAt,(uint32_t)totalKnown);
appendTo.setAt(addedAt,(uint16_t)added);
- //TRACE("..MC Multicaster::gather() attached %u of %u peers for %.16llx/%s (2)",n,(unsigned int)(gs->second.members.size() - skipped),nwid,mg.toString().c_str());
-
return added;
}
@@ -153,23 +159,66 @@ std::vector<Address> Multicaster::getMembers(uint64_t nwid,const MulticastGroup
void Multicaster::send(
void *tPtr,
- unsigned int limit,
- uint64_t now,
- uint64_t nwid,
- bool disableCompression,
- const std::vector<Address> &alwaysSendTo,
+ int64_t now,
+ const SharedPtr<Network> &network,
+ const Address &origin,
const MulticastGroup &mg,
const MAC &src,
unsigned int etherType,
const void *data,
unsigned int len)
{
- unsigned long idxbuf[8194];
+ unsigned long idxbuf[4096];
unsigned long *indexes = idxbuf;
+ // If we're in hub-and-spoke designated multicast replication mode, see if we
+ // have a multicast replicator active. If so, pick the best and send it
+ // there. If we are a multicast replicator or if none are alive, fall back
+ // to sender replication. Note that bridges do not do this since this would
+ // break bridge route learning. This is sort of an edge case limitation of
+ // the current protocol and could be fixed, but fixing it would add more
+ // complexity than the fix is probably worth. Bridges are generally high
+ // bandwidth nodes.
+ if (!network->config().isActiveBridge(RR->identity.address())) {
+ Address multicastReplicators[ZT_MAX_NETWORK_SPECIALISTS];
+ const unsigned int multicastReplicatorCount = network->config().multicastReplicators(multicastReplicators);
+ if (multicastReplicatorCount) {
+ if (std::find(multicastReplicators,multicastReplicators + multicastReplicatorCount,RR->identity.address()) == (multicastReplicators + multicastReplicatorCount)) {
+ SharedPtr<Peer> bestMulticastReplicator;
+ SharedPtr<Path> bestMulticastReplicatorPath;
+ unsigned int bestMulticastReplicatorLatency = 0xffff;
+ for(unsigned int i=0;i<multicastReplicatorCount;++i) {
+ const SharedPtr<Peer> p(RR->topology->getPeerNoCache(multicastReplicators[i]));
+ if ((p)&&(p->isAlive(now))) {
+ const SharedPtr<Path> pp(p->getBestPath(now,false));
+ if ((pp)&&(pp->latency() < bestMulticastReplicatorLatency)) {
+ bestMulticastReplicatorLatency = pp->latency();
+ bestMulticastReplicatorPath = pp;
+ bestMulticastReplicator = p;
+ }
+ }
+ }
+ if (bestMulticastReplicator) {
+ Packet outp(bestMulticastReplicator->address(),RR->identity.address(),Packet::VERB_MULTICAST_FRAME);
+ outp.append((uint64_t)network->id());
+ outp.append((uint8_t)0x0c); // includes source MAC | please replicate
+ ((src) ? src : MAC(RR->identity.address(),network->id())).appendTo(outp);
+ mg.mac().appendTo(outp);
+ outp.append((uint32_t)mg.adi());
+ outp.append((uint16_t)etherType);
+ outp.append(data,len);
+ if (!network->config().disableCompression()) outp.compress();
+ outp.armor(bestMulticastReplicator->key(),true);
+ bestMulticastReplicatorPath->send(RR,tPtr,outp.data(),outp.size(),now);
+ return;
+ }
+ }
+ }
+ }
+
try {
Mutex::Lock _l(_groups_m);
- MulticastGroupStatus &gs = _groups[Multicaster::Key(nwid,mg)];
+ MulticastGroupStatus &gs = _groups[Multicaster::Key(network->id(),mg)];
if (!gs.members.empty()) {
// Allocate a memory buffer if group is monstrous
@@ -187,6 +236,10 @@ void Multicaster::send(
}
}
+ Address activeBridges[ZT_MAX_NETWORK_SPECIALISTS];
+ const unsigned int activeBridgeCount = network->config().activeBridges(activeBridges);
+ const unsigned int limit = network->config().multicastLimit;
+
if (gs.members.size() >= limit) {
// Skip queue if we already have enough members to complete the send operation
OutboundMulticast out;
@@ -194,8 +247,8 @@ void Multicaster::send(
out.init(
RR,
now,
- nwid,
- disableCompression,
+ network->id(),
+ network->config().disableCompression(),
limit,
1, // we'll still gather a little from peers to keep multicast list fresh
src,
@@ -206,9 +259,9 @@ void Multicaster::send(
unsigned int count = 0;
- for(std::vector<Address>::const_iterator ast(alwaysSendTo.begin());ast!=alwaysSendTo.end();++ast) {
- if (*ast != RR->identity.address()) {
- out.sendOnly(RR,tPtr,*ast); // optimization: don't use dedup log if it's a one-pass send
+ for(unsigned int i=0;i<activeBridgeCount;++i) {
+ if ((activeBridges[i] != RR->identity.address())&&(activeBridges[i] != origin)) {
+ out.sendOnly(RR,tPtr,activeBridges[i]); // optimization: don't use dedup log if it's a one-pass send
if (++count >= limit)
break;
}
@@ -216,40 +269,59 @@ void Multicaster::send(
unsigned long idx = 0;
while ((count < limit)&&(idx < gs.members.size())) {
- Address ma(gs.members[indexes[idx++]].address);
- if (std::find(alwaysSendTo.begin(),alwaysSendTo.end(),ma) == alwaysSendTo.end()) {
+ const Address ma(gs.members[indexes[idx++]].address);
+ if ((std::find(activeBridges,activeBridges + activeBridgeCount,ma) == (activeBridges + activeBridgeCount))&&(ma != origin)) {
out.sendOnly(RR,tPtr,ma); // optimization: don't use dedup log if it's a one-pass send
++count;
}
}
} else {
- unsigned int gatherLimit = (limit - (unsigned int)gs.members.size()) + 1;
+ const unsigned int gatherLimit = (limit - (unsigned int)gs.members.size()) + 1;
if ((gs.members.empty())||((now - gs.lastExplicitGather) >= ZT_MULTICAST_EXPLICIT_GATHER_DELAY)) {
gs.lastExplicitGather = now;
Address explicitGatherPeers[16];
unsigned int numExplicitGatherPeers = 0;
+
SharedPtr<Peer> bestRoot(RR->topology->getUpstreamPeer());
if (bestRoot)
explicitGatherPeers[numExplicitGatherPeers++] = bestRoot->address();
- explicitGatherPeers[numExplicitGatherPeers++] = Network::controllerFor(nwid);
- SharedPtr<Network> network(RR->node->network(nwid));
- if (network) {
- std::vector<Address> anchors(network->config().anchors());
- for(std::vector<Address>::const_iterator a(anchors.begin());a!=anchors.end();++a) {
- if (*a != RR->identity.address()) {
- explicitGatherPeers[numExplicitGatherPeers++] = *a;
- if (numExplicitGatherPeers == 16)
- break;
- }
+
+ explicitGatherPeers[numExplicitGatherPeers++] = network->controller();
+
+ Address ac[ZT_MAX_NETWORK_SPECIALISTS];
+ const unsigned int accnt = network->config().alwaysContactAddresses(ac);
+ unsigned int shuffled[ZT_MAX_NETWORK_SPECIALISTS];
+ for(unsigned int i=0;i<accnt;++i)
+ shuffled[i] = i;
+ for(unsigned int i=0,k=accnt>>1;i<k;++i) {
+ const uint64_t x = RR->node->prng();
+ const unsigned int x1 = shuffled[(unsigned int)x % accnt];
+ const unsigned int x2 = shuffled[(unsigned int)(x >> 32) % accnt];
+ const unsigned int tmp = shuffled[x1];
+ shuffled[x1] = shuffled[x2];
+ shuffled[x2] = tmp;
+ }
+ for(unsigned int i=0;i<accnt;++i) {
+ explicitGatherPeers[numExplicitGatherPeers++] = ac[shuffled[i]];
+ if (numExplicitGatherPeers == 16)
+ break;
+ }
+
+ std::vector<Address> anchors(network->config().anchors());
+ for(std::vector<Address>::const_iterator a(anchors.begin());a!=anchors.end();++a) {
+ if (*a != RR->identity.address()) {
+ explicitGatherPeers[numExplicitGatherPeers++] = *a;
+ if (numExplicitGatherPeers == 16)
+ break;
}
}
for(unsigned int k=0;k<numExplicitGatherPeers;++k) {
const CertificateOfMembership *com = (network) ? ((network->config().com) ? &(network->config().com) : (const CertificateOfMembership *)0) : (const CertificateOfMembership *)0;
Packet outp(explicitGatherPeers[k],RR->identity.address(),Packet::VERB_MULTICAST_GATHER);
- outp.append(nwid);
+ outp.append(network->id());
outp.append((uint8_t)((com) ? 0x01 : 0x00));
mg.mac().appendTo(outp);
outp.append((uint32_t)mg.adi());
@@ -267,8 +339,8 @@ void Multicaster::send(
out.init(
RR,
now,
- nwid,
- disableCompression,
+ network->id(),
+ network->config().disableCompression(),
limit,
gatherLimit,
src,
@@ -277,11 +349,14 @@ void Multicaster::send(
data,
len);
+ if (origin)
+ out.logAsSent(origin);
+
unsigned int count = 0;
- for(std::vector<Address>::const_iterator ast(alwaysSendTo.begin());ast!=alwaysSendTo.end();++ast) {
- if (*ast != RR->identity.address()) {
- out.sendAndLog(RR,tPtr,*ast);
+ for(unsigned int i=0;i<activeBridgeCount;++i) {
+ if (activeBridges[i] != RR->identity.address()) {
+ out.sendAndLog(RR,tPtr,activeBridges[i]);
if (++count >= limit)
break;
}
@@ -290,7 +365,7 @@ void Multicaster::send(
unsigned long idx = 0;
while ((count < limit)&&(idx < gs.members.size())) {
Address ma(gs.members[indexes[idx++]].address);
- if (std::find(alwaysSendTo.begin(),alwaysSendTo.end(),ma) == alwaysSendTo.end()) {
+ if (std::find(activeBridges,activeBridges + activeBridgeCount,ma) == (activeBridges + activeBridgeCount)) {
out.sendAndLog(RR,tPtr,ma);
++count;
}
@@ -303,7 +378,7 @@ void Multicaster::send(
delete [] indexes;
}
-void Multicaster::clean(uint64_t now)
+void Multicaster::clean(int64_t now)
{
{
Mutex::Lock _l(_groups_m);
@@ -361,7 +436,7 @@ void Multicaster::addCredential(void *tPtr,const CertificateOfMembership &com,bo
}
}
-void Multicaster::_add(void *tPtr,uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member)
+void Multicaster::_add(void *tPtr,int64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member)
{
// assumes _groups_m is locked
@@ -378,8 +453,6 @@ void Multicaster::_add(void *tPtr,uint64_t now,uint64_t nwid,const MulticastGrou
gs.members.push_back(MulticastGroupMember(member,now));
- //TRACE("..MC %s joined multicast group %.16llx/%s via %s",member.toString().c_str(),nwid,mg.toString().c_str(),((learnedFrom) ? learnedFrom.toString().c_str() : "(direct)"));
-
for(std::list<OutboundMulticast>::iterator tx(gs.txQueue.begin());tx!=gs.txQueue.end();) {
if (tx->atLimit())
gs.txQueue.erase(tx++);
diff --git a/node/Multicaster.hpp b/node/Multicaster.hpp
index f646a5be..e57f81fe 100644
--- a/node/Multicaster.hpp
+++ b/node/Multicaster.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_MULTICASTER_HPP
@@ -34,50 +42,20 @@
#include "OutboundMulticast.hpp"
#include "Utils.hpp"
#include "Mutex.hpp"
-#include "NonCopyable.hpp"
+#include "SharedPtr.hpp"
namespace ZeroTier {
class RuntimeEnvironment;
class CertificateOfMembership;
class Packet;
+class Network;
/**
* Database of known multicast peers within a network
*/
-class Multicaster : NonCopyable
+class Multicaster
{
-private:
- struct Key
- {
- Key() : nwid(0),mg() {}
- Key(uint64_t n,const MulticastGroup &g) : nwid(n),mg(g) {}
-
- uint64_t nwid;
- MulticastGroup mg;
-
- inline bool operator==(const Key &k) const throw() { return ((nwid == k.nwid)&&(mg == k.mg)); }
- inline unsigned long hashCode() const throw() { return (mg.hashCode() ^ (unsigned long)(nwid ^ (nwid >> 32))); }
- };
-
- struct MulticastGroupMember
- {
- MulticastGroupMember() {}
- MulticastGroupMember(const Address &a,uint64_t ts) : address(a),timestamp(ts) {}
-
- Address address;
- uint64_t timestamp; // time of last notification
- };
-
- struct MulticastGroupStatus
- {
- MulticastGroupStatus() : lastExplicitGather(0) {}
-
- uint64_t lastExplicitGather;
- std::list<OutboundMulticast> txQueue; // pending outbound multicasts
- std::vector<MulticastGroupMember> members; // members of this group
- };
-
public:
Multicaster(const RuntimeEnvironment *renv);
~Multicaster();
@@ -90,7 +68,7 @@ public:
* @param mg Multicast group
* @param member New member address
*/
- inline void add(void *tPtr,uint64_t now,uint64_t nwid,const MulticastGroup &mg,const Address &member)
+ inline void add(void *tPtr,int64_t now,uint64_t nwid,const MulticastGroup &mg,const Address &member)
{
Mutex::Lock _l(_groups_m);
_add(tPtr,now,nwid,mg,_groups[Multicaster::Key(nwid,mg)],member);
@@ -109,7 +87,7 @@ public:
* @param count Number of addresses
* @param totalKnown Total number of known addresses as reported by peer
*/
- void addMultiple(void *tPtr,uint64_t now,uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,unsigned int totalKnown);
+ void addMultiple(void *tPtr,int64_t now,uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,unsigned int totalKnown);
/**
* Remove a multicast group member (if present)
@@ -152,11 +130,9 @@ public:
* Send a multicast
*
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
- * @param limit Multicast limit
* @param now Current time
- * @param nwid Network ID
- * @param disableCompression Disable packet payload compression?
- * @param alwaysSendTo Send to these peers first and even if not included in subscriber list
+ * @param network Network
+ * @param origin Origin of multicast (to not return to sender) or NULL if none
* @param mg Multicast group
* @param src Source Ethernet MAC address or NULL to skip in packet and compute from ZT address (non-bridged mode)
* @param etherType Ethernet frame type
@@ -165,11 +141,9 @@ public:
*/
void send(
void *tPtr,
- unsigned int limit,
- uint64_t now,
- uint64_t nwid,
- bool disableCompression,
- const std::vector<Address> &alwaysSendTo,
+ int64_t now,
+ const SharedPtr<Network> &network,
+ const Address &origin,
const MulticastGroup &mg,
const MAC &src,
unsigned int etherType,
@@ -182,7 +156,7 @@ public:
* @param RR Runtime environment
* @param now Current time
*/
- void clean(uint64_t now);
+ void clean(int64_t now);
/**
* Add an authorization credential
@@ -204,7 +178,7 @@ public:
* @param now Current time
* @return True if GATHER and LIKE should be allowed
*/
- bool cacheAuthorized(const Address &a,const uint64_t nwid,const uint64_t now) const
+ bool cacheAuthorized(const Address &a,const uint64_t nwid,const int64_t now) const
{
Mutex::Lock _l(_gatherAuth_m);
const uint64_t *p = _gatherAuth.get(_GatherAuthKey(nwid,a));
@@ -212,9 +186,39 @@ public:
}
private:
- void _add(void *tPtr,uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member);
+ struct Key
+ {
+ Key() : nwid(0),mg() {}
+ Key(uint64_t n,const MulticastGroup &g) : nwid(n),mg(g) {}
+
+ uint64_t nwid;
+ MulticastGroup mg;
+
+ inline bool operator==(const Key &k) const { return ((nwid == k.nwid)&&(mg == k.mg)); }
+ inline unsigned long hashCode() const { return (mg.hashCode() ^ (unsigned long)(nwid ^ (nwid >> 32))); }
+ };
+
+ struct MulticastGroupMember
+ {
+ MulticastGroupMember() {}
+ MulticastGroupMember(const Address &a,uint64_t ts) : address(a),timestamp(ts) {}
+
+ Address address;
+ uint64_t timestamp; // time of last notification
+ };
+
+ struct MulticastGroupStatus
+ {
+ MulticastGroupStatus() : lastExplicitGather(0) {}
+
+ uint64_t lastExplicitGather;
+ std::list<OutboundMulticast> txQueue; // pending outbound multicasts
+ std::vector<MulticastGroupMember> members; // members of this group
+ };
+
+ void _add(void *tPtr,int64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member);
- const RuntimeEnvironment *RR;
+ const RuntimeEnvironment *const RR;
Hashtable<Multicaster::Key,MulticastGroupStatus> _groups;
Mutex _groups_m;
diff --git a/node/Mutex.hpp b/node/Mutex.hpp
index d451ede0..a60a00b2 100644
--- a/node/Mutex.hpp
+++ b/node/Mutex.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,74 +14,126 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_MUTEX_HPP
#define ZT_MUTEX_HPP
#include "Constants.hpp"
-#include "NonCopyable.hpp"
#ifdef __UNIX_LIKE__
+#include <stdint.h>
#include <stdlib.h>
#include <pthread.h>
namespace ZeroTier {
-class Mutex : NonCopyable
+#if defined(__GNUC__) && (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64))
+
+// Inline ticket lock on x64 systems with GCC and CLANG (Mac, Linux) -- this is really fast as long as locking durations are very short
+class Mutex
{
public:
- Mutex()
- throw()
+ Mutex() :
+ nextTicket(0),
+ nowServing(0)
{
- pthread_mutex_init(&_mh,(const pthread_mutexattr_t *)0);
}
- ~Mutex()
+ inline void lock() const
{
- pthread_mutex_destroy(&_mh);
+ const uint16_t myTicket = __sync_fetch_and_add(&(const_cast<Mutex *>(this)->nextTicket),1);
+ while (nowServing != myTicket) {
+ __asm__ __volatile__("rep;nop"::);
+ __asm__ __volatile__("":::"memory");
+ }
}
- inline void lock()
- throw()
+ inline void unlock() const
{
- pthread_mutex_lock(&_mh);
+ ++(const_cast<Mutex *>(this)->nowServing);
}
- inline void unlock()
- throw()
+ /**
+ * Uses C++ contexts and constructor/destructor to lock/unlock automatically
+ */
+ class Lock
+ {
+ public:
+ Lock(Mutex &m) :
+ _m(&m)
+ {
+ m.lock();
+ }
+
+ Lock(const Mutex &m) :
+ _m(const_cast<Mutex *>(&m))
+ {
+ _m->lock();
+ }
+
+ ~Lock()
+ {
+ _m->unlock();
+ }
+
+ private:
+ Mutex *const _m;
+ };
+
+private:
+ Mutex(const Mutex &) {}
+ const Mutex &operator=(const Mutex &) { return *this; }
+
+ uint16_t nextTicket;
+ uint16_t nowServing;
+};
+
+#else
+
+// libpthread based mutex lock
+class Mutex
+{
+public:
+ Mutex()
{
- pthread_mutex_unlock(&_mh);
+ pthread_mutex_init(&_mh,(const pthread_mutexattr_t *)0);
+ }
+
+ ~Mutex()
+ {
+ pthread_mutex_destroy(&_mh);
}
inline void lock() const
- throw()
{
- (const_cast <Mutex *> (this))->lock();
+ pthread_mutex_lock(&((const_cast <Mutex *> (this))->_mh));
}
inline void unlock() const
- throw()
{
- (const_cast <Mutex *> (this))->unlock();
+ pthread_mutex_unlock(&((const_cast <Mutex *> (this))->_mh));
}
- /**
- * Uses C++ contexts and constructor/destructor to lock/unlock automatically
- */
- class Lock : NonCopyable
+ class Lock
{
public:
- Lock(Mutex &m)
- throw() :
+ Lock(Mutex &m) :
_m(&m)
{
m.lock();
}
- Lock(const Mutex &m)
- throw() :
+ Lock(const Mutex &m) :
_m(const_cast<Mutex *>(&m))
{
_m->lock();
@@ -97,9 +149,14 @@ public:
};
private:
+ Mutex(const Mutex &) {}
+ const Mutex &operator=(const Mutex &) { return *this; }
+
pthread_mutex_t _mh;
};
+#endif
+
} // namespace ZeroTier
#endif // Apple / Linux
@@ -111,11 +168,11 @@ private:
namespace ZeroTier {
-class Mutex : NonCopyable
+// Windows critical section based lock
+class Mutex
{
public:
Mutex()
- throw()
{
InitializeCriticalSection(&_cs);
}
@@ -126,41 +183,35 @@ public:
}
inline void lock()
- throw()
{
EnterCriticalSection(&_cs);
}
inline void unlock()
- throw()
{
LeaveCriticalSection(&_cs);
}
inline void lock() const
- throw()
{
(const_cast <Mutex *> (this))->lock();
}
inline void unlock() const
- throw()
{
(const_cast <Mutex *> (this))->unlock();
}
- class Lock : NonCopyable
+ class Lock
{
public:
- Lock(Mutex &m)
- throw() :
+ Lock(Mutex &m) :
_m(&m)
{
m.lock();
}
- Lock(const Mutex &m)
- throw() :
+ Lock(const Mutex &m) :
_m(const_cast<Mutex *>(&m))
{
_m->lock();
@@ -176,6 +227,9 @@ public:
};
private:
+ Mutex(const Mutex &) {}
+ const Mutex &operator=(const Mutex &) { return *this; }
+
CRITICAL_SECTION _cs;
};
diff --git a/node/Network.cpp b/node/Network.cpp
index b7f25f7f..a75d9fd1 100644
--- a/node/Network.cpp
+++ b/node/Network.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -34,90 +42,14 @@
#include "NetworkController.hpp"
#include "Node.hpp"
#include "Peer.hpp"
-#include "Cluster.hpp"
+#include "Trace.hpp"
-// Uncomment to make the rules engine dump trace info to stdout
-//#define ZT_RULES_ENGINE_DEBUGGING 1
+#include <set>
namespace ZeroTier {
namespace {
-#ifdef ZT_RULES_ENGINE_DEBUGGING
-#define FILTER_TRACE(f,...) { Utils::snprintf(dpbuf,sizeof(dpbuf),f,##__VA_ARGS__); dlog.push_back(std::string(dpbuf)); }
-static const char *_rtn(const ZT_VirtualNetworkRuleType rt)
-{
- switch(rt) {
- case ZT_NETWORK_RULE_ACTION_DROP: return "ACTION_DROP";
- case ZT_NETWORK_RULE_ACTION_ACCEPT: return "ACTION_ACCEPT";
- case ZT_NETWORK_RULE_ACTION_TEE: return "ACTION_TEE";
- case ZT_NETWORK_RULE_ACTION_WATCH: return "ACTION_WATCH";
- case ZT_NETWORK_RULE_ACTION_REDIRECT: return "ACTION_REDIRECT";
- case ZT_NETWORK_RULE_ACTION_BREAK: return "ACTION_BREAK";
- case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS: return "MATCH_SOURCE_ZEROTIER_ADDRESS";
- case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS: return "MATCH_DEST_ZEROTIER_ADDRESS";
- case ZT_NETWORK_RULE_MATCH_VLAN_ID: return "MATCH_VLAN_ID";
- case ZT_NETWORK_RULE_MATCH_VLAN_PCP: return "MATCH_VLAN_PCP";
- case ZT_NETWORK_RULE_MATCH_VLAN_DEI: return "MATCH_VLAN_DEI";
- case ZT_NETWORK_RULE_MATCH_MAC_SOURCE: return "MATCH_MAC_SOURCE";
- case ZT_NETWORK_RULE_MATCH_MAC_DEST: return "MATCH_MAC_DEST";
- case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE: return "MATCH_IPV4_SOURCE";
- case ZT_NETWORK_RULE_MATCH_IPV4_DEST: return "MATCH_IPV4_DEST";
- case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE: return "MATCH_IPV6_SOURCE";
- case ZT_NETWORK_RULE_MATCH_IPV6_DEST: return "MATCH_IPV6_DEST";
- case ZT_NETWORK_RULE_MATCH_IP_TOS: return "MATCH_IP_TOS";
- case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL: return "MATCH_IP_PROTOCOL";
- case ZT_NETWORK_RULE_MATCH_ETHERTYPE: return "MATCH_ETHERTYPE";
- case ZT_NETWORK_RULE_MATCH_ICMP: return "MATCH_ICMP";
- case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE: return "MATCH_IP_SOURCE_PORT_RANGE";
- case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE: return "MATCH_IP_DEST_PORT_RANGE";
- case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS: return "MATCH_CHARACTERISTICS";
- case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE: return "MATCH_FRAME_SIZE_RANGE";
- case ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE: return "MATCH_TAGS_DIFFERENCE";
- case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND: return "MATCH_TAGS_BITWISE_AND";
- case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR: return "MATCH_TAGS_BITWISE_OR";
- case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR: return "MATCH_TAGS_BITWISE_XOR";
- default: return "???";
- }
-}
-static const void _dumpFilterTrace(const char *ruleName,uint8_t thisSetMatches,bool inbound,const Address &ztSource,const Address &ztDest,const MAC &macSource,const MAC &macDest,const std::vector<std::string> &dlog,unsigned int frameLen,unsigned int etherType,const char *msg)
-{
- static volatile unsigned long cnt = 0;
- printf("%.6lu %c %s %s frameLen=%u etherType=%u" ZT_EOL_S,
- cnt++,
- ((thisSetMatches) ? 'Y' : '.'),
- ruleName,
- ((inbound) ? "INBOUND" : "OUTBOUND"),
- frameLen,
- etherType
- );
- for(std::vector<std::string>::const_iterator m(dlog.begin());m!=dlog.end();++m)
- printf(" | %s" ZT_EOL_S,m->c_str());
- printf(" + %c %s->%s %.2x:%.2x:%.2x:%.2x:%.2x:%.2x->%.2x:%.2x:%.2x:%.2x:%.2x:%.2x" ZT_EOL_S,
- ((thisSetMatches) ? 'Y' : '.'),
- ztSource.toString().c_str(),
- ztDest.toString().c_str(),
- (unsigned int)macSource[0],
- (unsigned int)macSource[1],
- (unsigned int)macSource[2],
- (unsigned int)macSource[3],
- (unsigned int)macSource[4],
- (unsigned int)macSource[5],
- (unsigned int)macDest[0],
- (unsigned int)macDest[1],
- (unsigned int)macDest[2],
- (unsigned int)macDest[3],
- (unsigned int)macDest[4],
- (unsigned int)macDest[5]
- );
- if (msg)
- printf(" + (%s)" ZT_EOL_S,msg);
- fflush(stdout);
-}
-#else
-#define FILTER_TRACE(f,...) {}
-#endif // ZT_RULES_ENGINE_DEBUGGING
-
// Returns true if packet appears valid; pos and proto will be set
static bool _ipv6GetPayload(const uint8_t *frameData,unsigned int frameLen,unsigned int &pos,unsigned int &proto)
{
@@ -155,8 +87,10 @@ enum _doZtFilterResult
DOZTFILTER_ACCEPT,
DOZTFILTER_SUPER_ACCEPT
};
+
static _doZtFilterResult _doZtFilter(
const RuntimeEnvironment *RR,
+ Trace::RuleResultLog &rrl,
const NetworkConfig &nconf,
const Membership *membership, // can be NULL
const bool inbound,
@@ -174,11 +108,6 @@ static _doZtFilterResult _doZtFilter(
unsigned int &ccLength, // MUTABLE -- set to length of packet payload to TEE
bool &ccWatch) // MUTABLE -- set to true for WATCH target as opposed to normal TEE
{
-#ifdef ZT_RULES_ENGINE_DEBUGGING
- char dpbuf[1024]; // used by FILTER_TRACE macro
- std::vector<std::string> dlog;
-#endif // ZT_RULES_ENGINE_DEBUGGING
-
// Set to true if we are a TEE/REDIRECT/WATCH target
bool superAccept = false;
@@ -186,6 +115,8 @@ static _doZtFilterResult _doZtFilter(
// ACTION with no MATCH entries preceding it is always taken.
uint8_t thisSetMatches = 1;
+ rrl.clear();
+
for(unsigned int rn=0;rn<ruleCount;++rn) {
const ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rules[rn].t & 0x3f);
@@ -194,15 +125,9 @@ static _doZtFilterResult _doZtFilter(
if (thisSetMatches) {
switch(rt) {
case ZT_NETWORK_RULE_ACTION_DROP:
-#ifdef ZT_RULES_ENGINE_DEBUGGING
- _dumpFilterTrace("ACTION_DROP",thisSetMatches,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,(const char *)0);
-#endif // ZT_RULES_ENGINE_DEBUGGING
return DOZTFILTER_DROP;
case ZT_NETWORK_RULE_ACTION_ACCEPT:
-#ifdef ZT_RULES_ENGINE_DEBUGGING
- _dumpFilterTrace("ACTION_ACCEPT",thisSetMatches,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,(const char *)0);
-#endif // ZT_RULES_ENGINE_DEBUGGING
return (superAccept ? DOZTFILTER_SUPER_ACCEPT : DOZTFILTER_ACCEPT); // match, accept packet
// These are initially handled together since preliminary logic is common
@@ -211,39 +136,18 @@ static _doZtFilterResult _doZtFilter(
case ZT_NETWORK_RULE_ACTION_REDIRECT: {
const Address fwdAddr(rules[rn].v.fwd.address);
if (fwdAddr == ztSource) {
-#ifdef ZT_RULES_ENGINE_DEBUGGING
- _dumpFilterTrace(_rtn(rt),thisSetMatches,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,"skipped as no-op since source is target");
- dlog.clear();
-#endif // ZT_RULES_ENGINE_DEBUGGING
+ // Skip as no-op since source is target
} else if (fwdAddr == RR->identity.address()) {
if (inbound) {
-#ifdef ZT_RULES_ENGINE_DEBUGGING
- _dumpFilterTrace(_rtn(rt),thisSetMatches,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,"interpreted as super-ACCEPT on inbound since we are target");
-#endif // ZT_RULES_ENGINE_DEBUGGING
return DOZTFILTER_SUPER_ACCEPT;
} else {
-#ifdef ZT_RULES_ENGINE_DEBUGGING
- _dumpFilterTrace(_rtn(rt),thisSetMatches,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,"skipped as no-op on outbound since we are target");
- dlog.clear();
-#endif // ZT_RULES_ENGINE_DEBUGGING
}
} else if (fwdAddr == ztDest) {
-#ifdef ZT_RULES_ENGINE_DEBUGGING
- _dumpFilterTrace(_rtn(rt),thisSetMatches,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,"skipped as no-op because destination is already target");
- dlog.clear();
-#endif // ZT_RULES_ENGINE_DEBUGGING
} else {
if (rt == ZT_NETWORK_RULE_ACTION_REDIRECT) {
-#ifdef ZT_RULES_ENGINE_DEBUGGING
- _dumpFilterTrace("ACTION_REDIRECT",thisSetMatches,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,(const char *)0);
-#endif // ZT_RULES_ENGINE_DEBUGGING
ztDest = fwdAddr;
return DOZTFILTER_REDIRECT;
} else {
-#ifdef ZT_RULES_ENGINE_DEBUGGING
- _dumpFilterTrace(_rtn(rt),thisSetMatches,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,(const char *)0);
- dlog.clear();
-#endif // ZT_RULES_ENGINE_DEBUGGING
cc = fwdAddr;
ccLength = (rules[rn].v.fwd.length != 0) ? ((frameLen < (unsigned int)rules[rn].v.fwd.length) ? frameLen : (unsigned int)rules[rn].v.fwd.length) : frameLen;
ccWatch = (rt == ZT_NETWORK_RULE_ACTION_WATCH);
@@ -252,18 +156,10 @@ static _doZtFilterResult _doZtFilter(
} continue;
case ZT_NETWORK_RULE_ACTION_BREAK:
-#ifdef ZT_RULES_ENGINE_DEBUGGING
- _dumpFilterTrace("ACTION_BREAK",thisSetMatches,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,(const char *)0);
- dlog.clear();
-#endif // ZT_RULES_ENGINE_DEBUGGING
return DOZTFILTER_NO_MATCH;
// Unrecognized ACTIONs are ignored as no-ops
default:
-#ifdef ZT_RULES_ENGINE_DEBUGGING
- _dumpFilterTrace(_rtn(rt),thisSetMatches,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,(const char *)0);
- dlog.clear();
-#endif // ZT_RULES_ENGINE_DEBUGGING
continue;
}
} else {
@@ -283,10 +179,6 @@ static _doZtFilterResult _doZtFilter(
}
}
-#ifdef ZT_RULES_ENGINE_DEBUGGING
- _dumpFilterTrace(_rtn(rt),thisSetMatches,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,(const char *)0);
- dlog.clear();
-#endif // ZT_RULES_ENGINE_DEBUGGING
thisSetMatches = 1; // reset to default true for next batch of entries
continue;
}
@@ -294,8 +186,10 @@ static _doZtFilterResult _doZtFilter(
// Circuit breaker: no need to evaluate an AND if the set's match state
// is currently false since anything AND false is false.
- if ((!thisSetMatches)&&(!(rules[rn].t & 0x40)))
+ if ((!thisSetMatches)&&(!(rules[rn].t & 0x40))) {
+ rrl.logSkipped(rn,thisSetMatches);
continue;
+ }
// If this was not an ACTION evaluate next MATCH and update thisSetMatches with (AND [result])
uint8_t thisRuleMatches = 0;
@@ -303,106 +197,82 @@ static _doZtFilterResult _doZtFilter(
switch(rt) {
case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
thisRuleMatches = (uint8_t)(rules[rn].v.zt == ztSource.toInt());
- FILTER_TRACE("%u %s %c %.10llx==%.10llx -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),rules[rn].v.zt,ztSource.toInt(),(unsigned int)thisRuleMatches);
break;
case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
thisRuleMatches = (uint8_t)(rules[rn].v.zt == ztDest.toInt());
- FILTER_TRACE("%u %s %c %.10llx==%.10llx -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),rules[rn].v.zt,ztDest.toInt(),(unsigned int)thisRuleMatches);
break;
case ZT_NETWORK_RULE_MATCH_VLAN_ID:
thisRuleMatches = (uint8_t)(rules[rn].v.vlanId == (uint16_t)vlanId);
- FILTER_TRACE("%u %s %c %u==%u -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.vlanId,(unsigned int)vlanId,(unsigned int)thisRuleMatches);
break;
case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
// NOT SUPPORTED YET
thisRuleMatches = (uint8_t)(rules[rn].v.vlanPcp == 0);
- FILTER_TRACE("%u %s %c %u==%u -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.vlanPcp,0,(unsigned int)thisRuleMatches);
break;
case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
// NOT SUPPORTED YET
thisRuleMatches = (uint8_t)(rules[rn].v.vlanDei == 0);
- FILTER_TRACE("%u %s %c %u==%u -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.vlanDei,0,(unsigned int)thisRuleMatches);
break;
case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
thisRuleMatches = (uint8_t)(MAC(rules[rn].v.mac,6) == macSource);
- FILTER_TRACE("%u %s %c %.12llx=%.12llx -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),rules[rn].v.mac,macSource.toInt(),(unsigned int)thisRuleMatches);
break;
case ZT_NETWORK_RULE_MATCH_MAC_DEST:
thisRuleMatches = (uint8_t)(MAC(rules[rn].v.mac,6) == macDest);
- FILTER_TRACE("%u %s %c %.12llx=%.12llx -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),rules[rn].v.mac,macDest.toInt(),(unsigned int)thisRuleMatches);
break;
case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
thisRuleMatches = (uint8_t)(InetAddress((const void *)&(rules[rn].v.ipv4.ip),4,rules[rn].v.ipv4.mask).containsAddress(InetAddress((const void *)(frameData + 12),4,0)));
- FILTER_TRACE("%u %s %c %s contains %s -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),InetAddress((const void *)&(rules[rn].v.ipv4.ip),4,rules[rn].v.ipv4.mask).toString().c_str(),InetAddress((const void *)(frameData + 12),4,0).toIpString().c_str(),(unsigned int)thisRuleMatches);
} else {
thisRuleMatches = 0;
- FILTER_TRACE("%u %s %c [frame not IPv4] -> 0",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='));
}
break;
case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
thisRuleMatches = (uint8_t)(InetAddress((const void *)&(rules[rn].v.ipv4.ip),4,rules[rn].v.ipv4.mask).containsAddress(InetAddress((const void *)(frameData + 16),4,0)));
- FILTER_TRACE("%u %s %c %s contains %s -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),InetAddress((const void *)&(rules[rn].v.ipv4.ip),4,rules[rn].v.ipv4.mask).toString().c_str(),InetAddress((const void *)(frameData + 16),4,0).toIpString().c_str(),(unsigned int)thisRuleMatches);
} else {
thisRuleMatches = 0;
- FILTER_TRACE("%u %s %c [frame not IPv4] -> 0",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='));
}
break;
case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
if ((etherType == ZT_ETHERTYPE_IPV6)&&(frameLen >= 40)) {
thisRuleMatches = (uint8_t)(InetAddress((const void *)rules[rn].v.ipv6.ip,16,rules[rn].v.ipv6.mask).containsAddress(InetAddress((const void *)(frameData + 8),16,0)));
- FILTER_TRACE("%u %s %c %s contains %s -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),InetAddress((const void *)rules[rn].v.ipv6.ip,16,rules[rn].v.ipv6.mask).toString().c_str(),InetAddress((const void *)(frameData + 8),16,0).toIpString().c_str(),(unsigned int)thisRuleMatches);
} else {
thisRuleMatches = 0;
- FILTER_TRACE("%u %s %c [frame not IPv6] -> 0",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='));
}
break;
case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
if ((etherType == ZT_ETHERTYPE_IPV6)&&(frameLen >= 40)) {
thisRuleMatches = (uint8_t)(InetAddress((const void *)rules[rn].v.ipv6.ip,16,rules[rn].v.ipv6.mask).containsAddress(InetAddress((const void *)(frameData + 24),16,0)));
- FILTER_TRACE("%u %s %c %s contains %s -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),InetAddress((const void *)rules[rn].v.ipv6.ip,16,rules[rn].v.ipv6.mask).toString().c_str(),InetAddress((const void *)(frameData + 24),16,0).toIpString().c_str(),(unsigned int)thisRuleMatches);
} else {
thisRuleMatches = 0;
- FILTER_TRACE("%u %s %c [frame not IPv6] -> 0",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='));
}
break;
case ZT_NETWORK_RULE_MATCH_IP_TOS:
if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
- //thisRuleMatches = (uint8_t)(rules[rn].v.ipTos == ((frameData[1] & 0xfc) >> 2));
const uint8_t tosMasked = frameData[1] & rules[rn].v.ipTos.mask;
thisRuleMatches = (uint8_t)((tosMasked >= rules[rn].v.ipTos.value[0])&&(tosMasked <= rules[rn].v.ipTos.value[1]));
- FILTER_TRACE("%u %s %c (IPv4) %u&%u==%u-%u -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)tosMasked,(unsigned int)rules[rn].v.ipTos.mask,(unsigned int)rules[rn].v.ipTos.value[0],(unsigned int)rules[rn].v.ipTos.value[1],(unsigned int)thisRuleMatches);
} else if ((etherType == ZT_ETHERTYPE_IPV6)&&(frameLen >= 40)) {
const uint8_t tosMasked = (((frameData[0] << 4) & 0xf0) | ((frameData[1] >> 4) & 0x0f)) & rules[rn].v.ipTos.mask;
thisRuleMatches = (uint8_t)((tosMasked >= rules[rn].v.ipTos.value[0])&&(tosMasked <= rules[rn].v.ipTos.value[1]));
- FILTER_TRACE("%u %s %c (IPv4) %u&%u==%u-%u -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)tosMasked,(unsigned int)rules[rn].v.ipTos.mask,(unsigned int)rules[rn].v.ipTos.value[0],(unsigned int)rules[rn].v.ipTos.value[1],(unsigned int)thisRuleMatches);
} else {
thisRuleMatches = 0;
- FILTER_TRACE("%u %s %c [frame not IP] -> 0",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='));
}
break;
case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
thisRuleMatches = (uint8_t)(rules[rn].v.ipProtocol == frameData[9]);
- FILTER_TRACE("%u %s %c (IPv4) %u==%u -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.ipProtocol,(unsigned int)frameData[9],(unsigned int)thisRuleMatches);
} else if (etherType == ZT_ETHERTYPE_IPV6) {
unsigned int pos = 0,proto = 0;
if (_ipv6GetPayload(frameData,frameLen,pos,proto)) {
thisRuleMatches = (uint8_t)(rules[rn].v.ipProtocol == (uint8_t)proto);
- FILTER_TRACE("%u %s %c (IPv6) %u==%u -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.ipProtocol,proto,(unsigned int)thisRuleMatches);
} else {
thisRuleMatches = 0;
- FILTER_TRACE("%u %s %c [invalid IPv6] -> 0",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='));
}
} else {
thisRuleMatches = 0;
- FILTER_TRACE("%u %s %c [frame not IP] -> 0",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='));
}
break;
case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
thisRuleMatches = (uint8_t)(rules[rn].v.etherType == (uint16_t)etherType);
- FILTER_TRACE("%u %s %c %u==%u -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.etherType,etherType,(unsigned int)thisRuleMatches);
break;
case ZT_NETWORK_RULE_MATCH_ICMP:
if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
@@ -418,14 +288,11 @@ static _doZtFilterResult _doZtFilter(
} else {
thisRuleMatches = 0;
}
- FILTER_TRACE("%u %s %c (IPv4) icmp-type:%d==%d icmp-code:%d==%d -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(int)frameData[ihl],(int)rules[rn].v.icmp.type,(int)frameData[ihl+1],(((rules[rn].v.icmp.flags & 0x01) != 0) ? (int)rules[rn].v.icmp.code : -1),(unsigned int)thisRuleMatches);
} else {
thisRuleMatches = 0;
- FILTER_TRACE("%u %s %c [IPv4 frame invalid] -> 0",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='));
}
} else {
thisRuleMatches = 0;
- FILTER_TRACE("%u %s %c [frame not ICMP] -> 0",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='));
}
} else if (etherType == ZT_ETHERTYPE_IPV6) {
unsigned int pos = 0,proto = 0;
@@ -440,21 +307,16 @@ static _doZtFilterResult _doZtFilter(
} else {
thisRuleMatches = 0;
}
- FILTER_TRACE("%u %s %c (IPv6) icmp-type:%d==%d icmp-code:%d==%d -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(int)frameData[pos],(int)rules[rn].v.icmp.type,(int)frameData[pos+1],(((rules[rn].v.icmp.flags & 0x01) != 0) ? (int)rules[rn].v.icmp.code : -1),(unsigned int)thisRuleMatches);
} else {
thisRuleMatches = 0;
- FILTER_TRACE("%u %s %c [frame not ICMPv6] -> 0",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='));
}
} else {
thisRuleMatches = 0;
- FILTER_TRACE("%u %s %c [invalid IPv6] -> 0",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='));
}
} else {
thisRuleMatches = 0;
- FILTER_TRACE("%u %s %c [frame not IP] -> 0",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='));
}
break;
- break;
case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
@@ -475,7 +337,6 @@ static _doZtFilterResult _doZtFilter(
}
thisRuleMatches = (p >= 0) ? (uint8_t)((p >= (int)rules[rn].v.port[0])&&(p <= (int)rules[rn].v.port[1])) : (uint8_t)0;
- FILTER_TRACE("%u %s %c (IPv4) %d in %d-%d -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),p,(int)rules[rn].v.port[0],(int)rules[rn].v.port[1],(unsigned int)thisRuleMatches);
} else if (etherType == ZT_ETHERTYPE_IPV6) {
unsigned int pos = 0,proto = 0;
if (_ipv6GetPayload(frameData,frameLen,pos,proto)) {
@@ -494,14 +355,11 @@ static _doZtFilterResult _doZtFilter(
break;
}
thisRuleMatches = (p > 0) ? (uint8_t)((p >= (int)rules[rn].v.port[0])&&(p <= (int)rules[rn].v.port[1])) : (uint8_t)0;
- FILTER_TRACE("%u %s %c (IPv6) %d in %d-%d -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),p,(int)rules[rn].v.port[0],(int)rules[rn].v.port[1],(unsigned int)thisRuleMatches);
} else {
thisRuleMatches = 0;
- FILTER_TRACE("%u %s %c [invalid IPv6] -> 0",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='));
}
} else {
thisRuleMatches = 0;
- FILTER_TRACE("%u %s %c [frame not IP] -> 0",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='));
}
break;
case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS: {
@@ -563,15 +421,12 @@ static _doZtFilterResult _doZtFilter(
}
}
thisRuleMatches = (uint8_t)((cf & rules[rn].v.characteristics) != 0);
- FILTER_TRACE("%u %s %c (%.16llx | %.16llx)!=0 -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),cf,rules[rn].v.characteristics,(unsigned int)thisRuleMatches);
} break;
case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
thisRuleMatches = (uint8_t)((frameLen >= (unsigned int)rules[rn].v.frameSize[0])&&(frameLen <= (unsigned int)rules[rn].v.frameSize[1]));
- FILTER_TRACE("%u %s %c %u in %u-%u -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),frameLen,(unsigned int)rules[rn].v.frameSize[0],(unsigned int)rules[rn].v.frameSize[1],(unsigned int)thisRuleMatches);
break;
case ZT_NETWORK_RULE_MATCH_RANDOM:
thisRuleMatches = (uint8_t)((uint32_t)(RR->node->prng() & 0xffffffffULL) <= rules[rn].v.randomProbability);
- FILTER_TRACE("%u %s %c -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)thisRuleMatches);
break;
case ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE:
case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND:
@@ -587,26 +442,20 @@ static _doZtFilterResult _doZtFilter(
if (rt == ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE) {
const uint32_t diff = (ltv > rtv) ? (ltv - rtv) : (rtv - ltv);
thisRuleMatches = (uint8_t)(diff <= rules[rn].v.tag.value);
- FILTER_TRACE("%u %s %c TAG %u local:%u remote:%u difference:%u<=%u -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id,ltv,rtv,diff,(unsigned int)rules[rn].v.tag.value,thisRuleMatches);
} else if (rt == ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND) {
thisRuleMatches = (uint8_t)((ltv & rtv) == rules[rn].v.tag.value);
- FILTER_TRACE("%u %s %c TAG %u local:%.8x & remote:%.8x == %.8x -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id,ltv,rtv,(unsigned int)rules[rn].v.tag.value,(unsigned int)thisRuleMatches);
} else if (rt == ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR) {
thisRuleMatches = (uint8_t)((ltv | rtv) == rules[rn].v.tag.value);
- FILTER_TRACE("%u %s %c TAG %u local:%.8x | remote:%.8x == %.8x -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id,ltv,rtv,(unsigned int)rules[rn].v.tag.value,(unsigned int)thisRuleMatches);
} else if (rt == ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR) {
thisRuleMatches = (uint8_t)((ltv ^ rtv) == rules[rn].v.tag.value);
- FILTER_TRACE("%u %s %c TAG %u local:%.8x ^ remote:%.8x == %.8x -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id,ltv,rtv,(unsigned int)rules[rn].v.tag.value,(unsigned int)thisRuleMatches);
} else if (rt == ZT_NETWORK_RULE_MATCH_TAGS_EQUAL) {
thisRuleMatches = (uint8_t)((ltv == rules[rn].v.tag.value)&&(rtv == rules[rn].v.tag.value));
- FILTER_TRACE("%u %s %c TAG %u local:%.8x and remote:%.8x == %.8x -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id,ltv,rtv,(unsigned int)rules[rn].v.tag.value,(unsigned int)thisRuleMatches);
} else { // sanity check, can't really happen
thisRuleMatches = 0;
}
} else {
if ((inbound)&&(!superAccept)) {
thisRuleMatches = 0;
- FILTER_TRACE("%u %s %c remote tag %u not found -> 0 (inbound side is strict)",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id);
} else {
// Outbound side is not strict since if we have to match both tags and
// we are sending a first packet to a recipient, we probably do not know
@@ -614,46 +463,67 @@ static _doZtFilterResult _doZtFilter(
// once we get their tag. If we are a tee/redirect target we are also
// not strict since we likely do not have these tags.
thisRuleMatches = 1;
- FILTER_TRACE("%u %s %c remote tag %u not found -> 1 (outbound side and TEE/REDIRECT targets are not strict)",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id);
}
}
} else {
thisRuleMatches = 0;
- FILTER_TRACE("%u %s %c local tag %u not found -> 0",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id);
}
} break;
case ZT_NETWORK_RULE_MATCH_TAG_SENDER:
case ZT_NETWORK_RULE_MATCH_TAG_RECEIVER: {
if (superAccept) {
thisRuleMatches = 1;
- FILTER_TRACE("%u %s %c we are a TEE/REDIRECT target -> 1",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='));
} else if ( ((rt == ZT_NETWORK_RULE_MATCH_TAG_SENDER)&&(inbound)) || ((rt == ZT_NETWORK_RULE_MATCH_TAG_RECEIVER)&&(!inbound)) ) {
const Tag *const remoteTag = ((membership) ? membership->getTag(nconf,rules[rn].v.tag.id) : (const Tag *)0);
if (remoteTag) {
thisRuleMatches = (uint8_t)(remoteTag->value() == rules[rn].v.tag.value);
- FILTER_TRACE("%u %s %c TAG %u %.8x == %.8x -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id,remoteTag->value(),(unsigned int)rules[rn].v.tag.value,(unsigned int)thisRuleMatches);
} else {
if (rt == ZT_NETWORK_RULE_MATCH_TAG_RECEIVER) {
// If we are checking the receiver and this is an outbound packet, we
// can't be strict since we may not yet know the receiver's tag.
thisRuleMatches = 1;
- FILTER_TRACE("%u %s %c (inbound) remote tag %u not found -> 1 (outbound receiver match is not strict)",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id);
} else {
thisRuleMatches = 0;
- FILTER_TRACE("%u %s %c (inbound) remote tag %u not found -> 0",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id);
}
}
} else { // sender and outbound or receiver and inbound
const Tag *const localTag = std::lower_bound(&(nconf.tags[0]),&(nconf.tags[nconf.tagCount]),rules[rn].v.tag.id,Tag::IdComparePredicate());
if ((localTag != &(nconf.tags[nconf.tagCount]))&&(localTag->id() == rules[rn].v.tag.id)) {
thisRuleMatches = (uint8_t)(localTag->value() == rules[rn].v.tag.value);
- FILTER_TRACE("%u %s %c TAG %u %.8x == %.8x -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id,localTag->value(),(unsigned int)rules[rn].v.tag.value,(unsigned int)thisRuleMatches);
} else {
thisRuleMatches = 0;
- FILTER_TRACE("%u %s %c local tag %u not found -> 0",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id);
}
}
} break;
+ case ZT_NETWORK_RULE_MATCH_INTEGER_RANGE: {
+ uint64_t integer = 0;
+ const unsigned int bits = (rules[rn].v.intRange.format & 63) + 1;
+ const unsigned int bytes = ((bits + 8 - 1) / 8); // integer ceiling of division by 8
+ if ((rules[rn].v.intRange.format & 0x80) == 0) {
+ // Big-endian
+ unsigned int idx = rules[rn].v.intRange.idx + (8 - bytes);
+ const unsigned int eof = idx + bytes;
+ if (eof <= frameLen) {
+ while (idx < eof) {
+ integer <<= 8;
+ integer |= frameData[idx++];
+ }
+ }
+ integer &= 0xffffffffffffffffULL >> (64 - bits);
+ } else {
+ // Little-endian
+ unsigned int idx = rules[rn].v.intRange.idx;
+ const unsigned int eof = idx + bytes;
+ if (eof <= frameLen) {
+ while (idx < eof) {
+ integer >>= 8;
+ integer |= ((uint64_t)frameData[idx++]) << 56;
+ }
+ }
+ integer >>= (64 - bits);
+ }
+ thisRuleMatches = (uint8_t)((integer >= rules[rn].v.intRange.start)&&(integer <= (rules[rn].v.intRange.start + (uint64_t)rules[rn].v.intRange.end)));
+ } break;
// The result of an unsupported MATCH is configurable at the network
// level via a flag.
@@ -662,6 +532,8 @@ static _doZtFilterResult _doZtFilter(
break;
}
+ rrl.log(rn,thisRuleMatches,thisSetMatches);
+
if ((rules[rn].t & 0x40))
thisSetMatches |= (thisRuleMatches ^ ((rules[rn].t >> 7) & 1));
else thisSetMatches &= (thisRuleMatches ^ ((rules[rn].t >> 7) & 1));
@@ -674,7 +546,7 @@ static _doZtFilterResult _doZtFilter(
const ZeroTier::MulticastGroup Network::BROADCAST(ZeroTier::MAC(0xffffffffffffULL),0);
-Network::Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *uptr) :
+Network::Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *uptr,const NetworkConfig *nconf) :
RR(renv),
_uPtr(uptr),
_id(nwid),
@@ -689,29 +561,33 @@ Network::Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *u
for(int i=0;i<ZT_NETWORK_MAX_INCOMING_UPDATES;++i)
_incomingConfigChunks[i].ts = 0;
- char confn[128];
- Utils::snprintf(confn,sizeof(confn),"networks.d/%.16llx.conf",_id);
-
- bool gotConf = false;
- Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dconf = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
- NetworkConfig *nconf = new NetworkConfig();
- try {
- std::string conf(RR->node->dataStoreGet(tPtr,confn));
- if (conf.length()) {
- dconf->load(conf.c_str());
- if (nconf->fromDictionary(*dconf)) {
- this->setConfiguration(tPtr,*nconf,false);
- _lastConfigUpdate = 0; // we still want to re-request a new config from the network
- gotConf = true;
+ if (nconf) {
+ this->setConfiguration(tPtr,*nconf,false);
+ _lastConfigUpdate = 0; // still want to re-request since it's likely outdated
+ } else {
+ uint64_t tmp[2];
+ tmp[0] = nwid; tmp[1] = 0;
+
+ bool got = false;
+ Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dict = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
+ try {
+ int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_NETWORK_CONFIG,tmp,dict->unsafeData(),ZT_NETWORKCONFIG_DICT_CAPACITY - 1);
+ if (n > 1) {
+ NetworkConfig *nconf = new NetworkConfig();
+ try {
+ if (nconf->fromDictionary(*dict)) {
+ this->setConfiguration(tPtr,*nconf,false);
+ _lastConfigUpdate = 0; // still want to re-request an update since it's likely outdated
+ got = true;
+ }
+ } catch ( ... ) {}
+ delete nconf;
}
- }
- } catch ( ... ) {} // ignore invalids, we'll re-request
- delete nconf;
- delete dconf;
+ } catch ( ... ) {}
+ delete dict;
- if (!gotConf) {
- // Save a one-byte CR to persist membership while we request a real netconf
- RR->node->dataStorePut(tPtr,confn,"\n",1,false);
+ if (!got)
+ RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_NETWORK_CONFIG,tmp,"\n",1);
}
if (!_portInitialized) {
@@ -727,12 +603,9 @@ Network::~Network()
ZT_VirtualNetworkConfig ctmp;
_externalConfig(&ctmp);
- char n[128];
if (_destroyed) {
- // This is done in Node::leave() so we can pass tPtr
+ // This is done in Node::leave() so we can pass tPtr properly
//RR->node->configureVirtualNetworkPort((void *)0,_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY,&ctmp);
- Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.conf",_id);
- RR->node->dataStoreDelete((void *)0,n);
} else {
RR->node->configureVirtualNetworkPort((void *)0,_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN,&ctmp);
}
@@ -750,36 +623,37 @@ bool Network::filterOutgoingPacket(
const unsigned int etherType,
const unsigned int vlanId)
{
- const uint64_t now = RR->node->now();
+ const int64_t now = RR->node->now();
Address ztFinalDest(ztDest);
int localCapabilityIndex = -1;
- bool accept = false;
+ int accept = 0;
+ Trace::RuleResultLog rrl,crrl;
+ Address cc;
+ unsigned int ccLength = 0;
+ bool ccWatch = false;
Mutex::Lock _l(_lock);
Membership *const membership = (ztDest) ? _memberships.get(ztDest) : (Membership *)0;
- Address cc;
- unsigned int ccLength = 0;
- bool ccWatch = false;
- switch(_doZtFilter(RR,_config,membership,false,ztSource,ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,cc,ccLength,ccWatch)) {
+ switch(_doZtFilter(RR,rrl,_config,membership,false,ztSource,ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,cc,ccLength,ccWatch)) {
- case DOZTFILTER_NO_MATCH:
+ case DOZTFILTER_NO_MATCH: {
for(unsigned int c=0;c<_config.capabilityCount;++c) {
ztFinalDest = ztDest; // sanity check, shouldn't be possible if there was no match
Address cc2;
unsigned int ccLength2 = 0;
bool ccWatch2 = false;
- switch (_doZtFilter(RR,_config,membership,false,ztSource,ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.capabilities[c].rules(),_config.capabilities[c].ruleCount(),cc2,ccLength2,ccWatch2)) {
+ switch (_doZtFilter(RR,crrl,_config,membership,false,ztSource,ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.capabilities[c].rules(),_config.capabilities[c].ruleCount(),cc2,ccLength2,ccWatch2)) {
case DOZTFILTER_NO_MATCH:
case DOZTFILTER_DROP: // explicit DROP in a capability just terminates its evaluation and is an anti-pattern
break;
case DOZTFILTER_REDIRECT: // interpreted as ACCEPT but ztFinalDest will have been changed in _doZtFilter()
case DOZTFILTER_ACCEPT:
- case DOZTFILTER_SUPER_ACCEPT: // no difference in behavior on outbound side
+ case DOZTFILTER_SUPER_ACCEPT: // no difference in behavior on outbound side in capabilities
localCapabilityIndex = (int)c;
- accept = true;
+ accept = 1;
if ((!noTee)&&(cc2)) {
Membership &m2 = _membership(cc2);
@@ -801,15 +675,20 @@ bool Network::filterOutgoingPacket(
if (accept)
break;
}
- break;
+ } break;
case DOZTFILTER_DROP:
+ if (_config.remoteTraceTarget)
+ RR->t->networkFilter(tPtr,*this,rrl,(Trace::RuleResultLog *)0,(Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,0);
return false;
case DOZTFILTER_REDIRECT: // interpreted as ACCEPT but ztFinalDest will have been changed in _doZtFilter()
case DOZTFILTER_ACCEPT:
- case DOZTFILTER_SUPER_ACCEPT: // no difference in behavior on outbound side
- accept = true;
+ accept = 1;
+ break;
+
+ case DOZTFILTER_SUPER_ACCEPT:
+ accept = 2;
break;
}
@@ -846,11 +725,17 @@ bool Network::filterOutgoingPacket(
outp.compress();
RR->sw->send(tPtr,outp,true);
+ if (_config.remoteTraceTarget)
+ RR->t->networkFilter(tPtr,*this,rrl,(localCapabilityIndex >= 0) ? &crrl : (Trace::RuleResultLog *)0,(localCapabilityIndex >= 0) ? &(_config.capabilities[localCapabilityIndex]) : (Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,0);
return false; // DROP locally, since we redirected
} else {
+ if (_config.remoteTraceTarget)
+ RR->t->networkFilter(tPtr,*this,rrl,(localCapabilityIndex >= 0) ? &crrl : (Trace::RuleResultLog *)0,(localCapabilityIndex >= 0) ? &(_config.capabilities[localCapabilityIndex]) : (Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,1);
return true;
}
} else {
+ if (_config.remoteTraceTarget)
+ RR->t->networkFilter(tPtr,*this,rrl,(localCapabilityIndex >= 0) ? &crrl : (Trace::RuleResultLog *)0,(localCapabilityIndex >= 0) ? &(_config.capabilities[localCapabilityIndex]) : (Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,0);
return false;
}
}
@@ -867,26 +752,27 @@ int Network::filterIncomingPacket(
const unsigned int vlanId)
{
Address ztFinalDest(ztDest);
+ Trace::RuleResultLog rrl,crrl;
int accept = 0;
+ Address cc;
+ unsigned int ccLength = 0;
+ bool ccWatch = false;
+ const Capability *c = (Capability *)0;
Mutex::Lock _l(_lock);
Membership &membership = _membership(sourcePeer->address());
- Address cc;
- unsigned int ccLength = 0;
- bool ccWatch = false;
- switch (_doZtFilter(RR,_config,&membership,true,sourcePeer->address(),ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,cc,ccLength,ccWatch)) {
+ switch (_doZtFilter(RR,rrl,_config,&membership,true,sourcePeer->address(),ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,cc,ccLength,ccWatch)) {
case DOZTFILTER_NO_MATCH: {
Membership::CapabilityIterator mci(membership,_config);
- const Capability *c;
while ((c = mci.next())) {
ztFinalDest = ztDest; // sanity check, should be unmodified if there was no match
Address cc2;
unsigned int ccLength2 = 0;
bool ccWatch2 = false;
- switch(_doZtFilter(RR,_config,&membership,true,sourcePeer->address(),ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,c->rules(),c->ruleCount(),cc2,ccLength2,ccWatch2)) {
+ switch(_doZtFilter(RR,crrl,_config,&membership,true,sourcePeer->address(),ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,c->rules(),c->ruleCount(),cc2,ccLength2,ccWatch2)) {
case DOZTFILTER_NO_MATCH:
case DOZTFILTER_DROP: // explicit DROP in a capability just terminates its evaluation and is an anti-pattern
break;
@@ -919,6 +805,8 @@ int Network::filterIncomingPacket(
} break;
case DOZTFILTER_DROP:
+ if (_config.remoteTraceTarget)
+ RR->t->networkFilter(tPtr,*this,rrl,(Trace::RuleResultLog *)0,(Capability *)0,sourcePeer->address(),ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,false,true,0);
return 0; // DROP
case DOZTFILTER_REDIRECT: // interpreted as ACCEPT but ztFinalDest will have been changed in _doZtFilter()
@@ -958,10 +846,14 @@ int Network::filterIncomingPacket(
outp.compress();
RR->sw->send(tPtr,outp,true);
+ if (_config.remoteTraceTarget)
+ RR->t->networkFilter(tPtr,*this,rrl,(c) ? &crrl : (Trace::RuleResultLog *)0,c,sourcePeer->address(),ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,false,true,0);
return 0; // DROP locally, since we redirected
}
}
+ if (_config.remoteTraceTarget)
+ RR->t->networkFilter(tPtr,*this,rrl,(c) ? &crrl : (Trace::RuleResultLog *)0,c,sourcePeer->address(),ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,false,true,accept);
return accept;
}
@@ -1017,15 +909,10 @@ uint64_t Network::handleConfigChunk(void *tPtr,const uint64_t packetId,const Add
totalLength = chunk.at<uint32_t>(ptr); ptr += 4;
chunkIndex = chunk.at<uint32_t>(ptr); ptr += 4;
- if (((chunkIndex + chunkLen) > totalLength)||(totalLength >= ZT_NETWORKCONFIG_DICT_CAPACITY)) { // >= since we need room for a null at the end
- TRACE("discarded chunk from %s: invalid length or length overflow",source.toString().c_str());
+ if (((chunkIndex + chunkLen) > totalLength)||(totalLength >= ZT_NETWORKCONFIG_DICT_CAPACITY)) // >= since we need room for a null at the end
return 0;
- }
-
- if ((chunk[ptr] != 1)||(chunk.at<uint16_t>(ptr + 1) != ZT_C25519_SIGNATURE_LEN)) {
- TRACE("discarded chunk from %s: unrecognized signature type",source.toString().c_str());
+ if ((chunk[ptr] != 1)||(chunk.at<uint16_t>(ptr + 1) != ZT_C25519_SIGNATURE_LEN))
return 0;
- }
const uint8_t *sig = reinterpret_cast<const uint8_t *>(chunk.field(ptr + 3,ZT_C25519_SIGNATURE_LEN));
// We can use the signature, which is unique per chunk, to get a per-chunk ID for local deduplication use
@@ -1050,19 +937,10 @@ uint64_t Network::handleConfigChunk(void *tPtr,const uint64_t packetId,const Add
// If it's not a duplicate, check chunk signature
const Identity controllerId(RR->topology->getIdentity(tPtr,controller()));
- if (!controllerId) { // we should always have the controller identity by now, otherwise how would we have queried it the first time?
- TRACE("unable to verify chunk from %s: don't have controller identity",source.toString().c_str());
+ if (!controllerId) // we should always have the controller identity by now, otherwise how would we have queried it the first time?
return 0;
- }
- if (!controllerId.verify(chunk.field(start,ptr - start),ptr - start,sig,ZT_C25519_SIGNATURE_LEN)) {
- TRACE("discarded chunk from %s: signature check failed",source.toString().c_str());
+ if (!controllerId.verify(chunk.field(start,ptr - start),ptr - start,sig,ZT_C25519_SIGNATURE_LEN))
return 0;
- }
-
-#ifdef ZT_ENABLE_CLUSTER
- if ((source)&&(RR->cluster))
- RR->cluster->broadcastNetworkConfigChunk(chunk.field(start,chunk.size() - start),chunk.size() - start);
-#endif
// New properly verified chunks can be flooded "virally" through the network
if (fastPropagate) {
@@ -1091,13 +969,8 @@ uint64_t Network::handleConfigChunk(void *tPtr,const uint64_t packetId,const Add
if ((!c)||(_incomingConfigChunks[i].ts < c->ts))
c = &(_incomingConfigChunks[i]);
}
-
-#ifdef ZT_ENABLE_CLUSTER
- if ((source)&&(RR->cluster))
- RR->cluster->broadcastNetworkConfigChunk(chunk.field(start,chunk.size() - start),chunk.size() - start);
-#endif
} else {
- TRACE("discarded single-chunk unsigned legacy config: this is only allowed if the sender is the controller itself");
+ // Single-chunk unsigned legacy configs are only allowed from the controller itself
return 0;
}
@@ -1112,7 +985,7 @@ uint64_t Network::handleConfigChunk(void *tPtr,const uint64_t packetId,const Add
return false;
c->haveChunkIds[c->haveChunks++] = chunkId;
- memcpy(c->data.unsafeData() + chunkIndex,chunkData,chunkLen);
+ ZT_FAST_MEMCPY(c->data.unsafeData() + chunkIndex,chunkData,chunkLen);
c->haveBytes += chunkLen;
if (c->haveBytes == totalLength) {
@@ -1180,18 +1053,17 @@ int Network::setConfiguration(void *tPtr,const NetworkConfig &nconf,bool saveToD
if (saveToDisk) {
Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *d = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
try {
- char n[64];
- Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.conf",_id);
- if (nconf.toDictionary(*d,false))
- RR->node->dataStorePut(tPtr,n,(const void *)d->data(),d->sizeBytes(),true);
+ if (nconf.toDictionary(*d,false)) {
+ uint64_t tmp[2];
+ tmp[0] = _id; tmp[1] = 0;
+ RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_NETWORK_CONFIG,tmp,d->data(),d->sizeBytes());
+ }
} catch ( ... ) {}
delete d;
}
return 2; // OK and configuration has changed
- } catch ( ... ) {
- TRACE("ignored invalid configuration for network %.16llx",(unsigned long long)_id);
- }
+ } catch ( ... ) {} // ignore invalid configs
return 0;
}
@@ -1200,15 +1072,91 @@ void Network::requestConfiguration(void *tPtr)
if (_destroyed)
return;
- /* ZeroTier addresses can't begin with 0xff, so this is used to mark controllerless
- * network IDs. Controllerless network IDs only support unicast IPv6 using the 6plane
- * addressing scheme and have the following format: 0xffSSSSEEEE000000 where SSSS
- * is the 16-bit starting IP port range allowed and EEEE is the 16-bit ending IP port
- * range allowed. Remaining digits are reserved for future use and must be zero. */
if ((_id >> 56) == 0xff) {
- const uint16_t startPortRange = (uint16_t)((_id >> 40) & 0xffff);
- const uint16_t endPortRange = (uint16_t)((_id >> 24) & 0xffff);
- if (((_id & 0xffffff) == 0)&&(endPortRange >= startPortRange)) {
+ if ((_id & 0xffffff) == 0) {
+ const uint16_t startPortRange = (uint16_t)((_id >> 40) & 0xffff);
+ const uint16_t endPortRange = (uint16_t)((_id >> 24) & 0xffff);
+ if (endPortRange >= startPortRange) {
+ NetworkConfig *const nconf = new NetworkConfig();
+
+ nconf->networkId = _id;
+ nconf->timestamp = RR->node->now();
+ nconf->credentialTimeMaxDelta = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA;
+ nconf->revision = 1;
+ nconf->issuedTo = RR->identity.address();
+ nconf->flags = ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION;
+ nconf->mtu = ZT_DEFAULT_MTU;
+ nconf->multicastLimit = 0;
+ nconf->staticIpCount = 1;
+ nconf->ruleCount = 14;
+ nconf->staticIps[0] = InetAddress::makeIpv66plane(_id,RR->identity.address().toInt());
+
+ // Drop everything but IPv6
+ nconf->rules[0].t = (uint8_t)ZT_NETWORK_RULE_MATCH_ETHERTYPE | 0x80; // NOT
+ nconf->rules[0].v.etherType = 0x86dd; // IPv6
+ nconf->rules[1].t = (uint8_t)ZT_NETWORK_RULE_ACTION_DROP;
+
+ // Allow ICMPv6
+ nconf->rules[2].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_PROTOCOL;
+ nconf->rules[2].v.ipProtocol = 0x3a; // ICMPv6
+ nconf->rules[3].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
+
+ // Allow destination ports within range
+ nconf->rules[4].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_PROTOCOL;
+ nconf->rules[4].v.ipProtocol = 0x11; // UDP
+ nconf->rules[5].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_PROTOCOL | 0x40; // OR
+ nconf->rules[5].v.ipProtocol = 0x06; // TCP
+ nconf->rules[6].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE;
+ nconf->rules[6].v.port[0] = startPortRange;
+ nconf->rules[6].v.port[1] = endPortRange;
+ nconf->rules[7].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
+
+ // Allow non-SYN TCP packets to permit non-connection-initiating traffic
+ nconf->rules[8].t = (uint8_t)ZT_NETWORK_RULE_MATCH_CHARACTERISTICS | 0x80; // NOT
+ nconf->rules[8].v.characteristics = ZT_RULE_PACKET_CHARACTERISTICS_TCP_SYN;
+ nconf->rules[9].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
+
+ // Also allow SYN+ACK which are replies to SYN
+ nconf->rules[10].t = (uint8_t)ZT_NETWORK_RULE_MATCH_CHARACTERISTICS;
+ nconf->rules[10].v.characteristics = ZT_RULE_PACKET_CHARACTERISTICS_TCP_SYN;
+ nconf->rules[11].t = (uint8_t)ZT_NETWORK_RULE_MATCH_CHARACTERISTICS;
+ nconf->rules[11].v.characteristics = ZT_RULE_PACKET_CHARACTERISTICS_TCP_ACK;
+ nconf->rules[12].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
+
+ nconf->rules[13].t = (uint8_t)ZT_NETWORK_RULE_ACTION_DROP;
+
+ nconf->type = ZT_NETWORK_TYPE_PUBLIC;
+
+ nconf->name[0] = 'a';
+ nconf->name[1] = 'd';
+ nconf->name[2] = 'h';
+ nconf->name[3] = 'o';
+ nconf->name[4] = 'c';
+ nconf->name[5] = '-';
+ Utils::hex((uint16_t)startPortRange,nconf->name + 6);
+ nconf->name[10] = '-';
+ Utils::hex((uint16_t)endPortRange,nconf->name + 11);
+ nconf->name[15] = (char)0;
+
+ this->setConfiguration(tPtr,*nconf,false);
+ delete nconf;
+ } else {
+ this->setNotFound();
+ }
+ } else if ((_id & 0xff) == 0x01) {
+ // ffAAaaaaaaaaaa01 -- where AA is the IPv4 /8 to use and aaaaaaaaaa is the anchor node for multicast gather and replication
+ const uint64_t myAddress = RR->identity.address().toInt();
+ const uint64_t networkHub = (_id >> 8) & 0xffffffffffULL;
+
+ uint8_t ipv4[4];
+ ipv4[0] = (uint8_t)((_id >> 48) & 0xff);
+ ipv4[1] = (uint8_t)((myAddress >> 16) & 0xff);
+ ipv4[2] = (uint8_t)((myAddress >> 8) & 0xff);
+ ipv4[3] = (uint8_t)(myAddress & 0xff);
+
+ char v4ascii[24];
+ Utils::decimal(ipv4[0],v4ascii);
+
NetworkConfig *const nconf = new NetworkConfig();
nconf->networkId = _id;
@@ -1217,51 +1165,40 @@ void Network::requestConfiguration(void *tPtr)
nconf->revision = 1;
nconf->issuedTo = RR->identity.address();
nconf->flags = ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION;
- nconf->staticIpCount = 1;
- nconf->ruleCount = 14;
- nconf->staticIps[0] = InetAddress::makeIpv66plane(_id,RR->identity.address().toInt());
-
- // Drop everything but IPv6
- nconf->rules[0].t = (uint8_t)ZT_NETWORK_RULE_MATCH_ETHERTYPE | 0x80; // NOT
- nconf->rules[0].v.etherType = 0x86dd; // IPv6
- nconf->rules[1].t = (uint8_t)ZT_NETWORK_RULE_ACTION_DROP;
-
- // Allow ICMPv6
- nconf->rules[2].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_PROTOCOL;
- nconf->rules[2].v.ipProtocol = 0x3a; // ICMPv6
- nconf->rules[3].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
-
- // Allow destination ports within range
- nconf->rules[4].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_PROTOCOL;
- nconf->rules[4].v.ipProtocol = 0x11; // UDP
- nconf->rules[5].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_PROTOCOL | 0x40; // OR
- nconf->rules[5].v.ipProtocol = 0x06; // TCP
- nconf->rules[6].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE;
- nconf->rules[6].v.port[0] = startPortRange;
- nconf->rules[6].v.port[1] = endPortRange;
- nconf->rules[7].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
-
- // Allow non-SYN TCP packets to permit non-connection-initiating traffic
- nconf->rules[8].t = (uint8_t)ZT_NETWORK_RULE_MATCH_CHARACTERISTICS | 0x80; // NOT
- nconf->rules[8].v.characteristics = ZT_RULE_PACKET_CHARACTERISTICS_TCP_SYN;
- nconf->rules[9].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
-
- // Also allow SYN+ACK which are replies to SYN
- nconf->rules[10].t = (uint8_t)ZT_NETWORK_RULE_MATCH_CHARACTERISTICS;
- nconf->rules[10].v.characteristics = ZT_RULE_PACKET_CHARACTERISTICS_TCP_SYN;
- nconf->rules[11].t = (uint8_t)ZT_NETWORK_RULE_MATCH_CHARACTERISTICS;
- nconf->rules[11].v.characteristics = ZT_RULE_PACKET_CHARACTERISTICS_TCP_ACK;
- nconf->rules[12].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
-
- nconf->rules[13].t = (uint8_t)ZT_NETWORK_RULE_ACTION_DROP;
+ nconf->mtu = ZT_DEFAULT_MTU;
+ nconf->multicastLimit = 1024;
+ nconf->specialistCount = (networkHub == 0) ? 0 : 1;
+ nconf->staticIpCount = 2;
+ nconf->ruleCount = 1;
+
+ if (networkHub != 0)
+ nconf->specialists[0] = networkHub;
+
+ nconf->staticIps[0] = InetAddress::makeIpv66plane(_id,myAddress);
+ nconf->staticIps[1].set(ipv4,4,8);
+
+ nconf->rules[0].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
nconf->type = ZT_NETWORK_TYPE_PUBLIC;
- Utils::snprintf(nconf->name,sizeof(nconf->name),"adhoc-%.04x-%.04x",(int)startPortRange,(int)endPortRange);
+
+ nconf->name[0] = 'a';
+ nconf->name[1] = 'd';
+ nconf->name[2] = 'h';
+ nconf->name[3] = 'o';
+ nconf->name[4] = 'c';
+ nconf->name[5] = '-';
+ unsigned long nn = 6;
+ while ((nconf->name[nn] = v4ascii[nn - 6])) ++nn;
+ nconf->name[nn++] = '.';
+ nconf->name[nn++] = '0';
+ nconf->name[nn++] = '.';
+ nconf->name[nn++] = '0';
+ nconf->name[nn++] = '.';
+ nconf->name[nn++] = '0';
+ nconf->name[nn++] = (char)0;
this->setConfiguration(tPtr,*nconf,false);
delete nconf;
- } else {
- this->setNotFound();
}
return;
}
@@ -1282,6 +1219,8 @@ void Network::requestConfiguration(void *tPtr)
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_FLAGS,(uint64_t)0);
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_RULES_ENGINE_REV,(uint64_t)ZT_RULES_ENGINE_REVISION);
+ RR->t->networkConfigRequestSent(tPtr,*this,ctrl);
+
if (ctrl == RR->identity.address()) {
if (RR->localNetworkController) {
RR->localNetworkController->request(_id,InetAddress(),0xffffffffffffffffULL,RR->identity,rmd);
@@ -1291,8 +1230,6 @@ void Network::requestConfiguration(void *tPtr)
return;
}
- TRACE("requesting netconf for network %.16llx from controller %s",(unsigned long long)_id,ctrl.toString().c_str());
-
Packet outp(ctrl,RR->identity.address(),Packet::VERB_NETWORK_CONFIG_REQUEST);
outp.append((uint64_t)_id);
const unsigned int rmdSize = rmd.sizeBytes();
@@ -1311,7 +1248,7 @@ void Network::requestConfiguration(void *tPtr)
bool Network::gate(void *tPtr,const SharedPtr<Peer> &peer)
{
- const uint64_t now = RR->node->now();
+ const int64_t now = RR->node->now();
Mutex::Lock _l(_lock);
try {
if (_config) {
@@ -1326,15 +1263,20 @@ bool Network::gate(void *tPtr,const SharedPtr<Peer> &peer)
return true;
}
}
- } catch ( ... ) {
- TRACE("gate() check failed for peer %s: unexpected exception",peer->address().toString().c_str());
- }
+ } catch ( ... ) {}
return false;
}
+bool Network::recentlyAssociatedWith(const Address &addr)
+{
+ Mutex::Lock _l(_lock);
+ const Membership *m = _memberships.get(addr);
+ return ((m)&&(m->recentlyAssociated(RR->node->now())));
+}
+
void Network::clean()
{
- const uint64_t now = RR->node->now();
+ const int64_t now = RR->node->now();
Mutex::Lock _l(_lock);
if (_destroyed)
@@ -1399,7 +1341,7 @@ void Network::learnBridgeRoute(const MAC &mac,const Address &addr)
}
}
-void Network::learnBridgedMulticastGroup(void *tPtr,const MulticastGroup &mg,uint64_t now)
+void Network::learnBridgedMulticastGroup(void *tPtr,const MulticastGroup &mg,int64_t now)
{
Mutex::Lock _l(_lock);
const unsigned long tmp = (unsigned long)_multicastGroupsBehindMe.size();
@@ -1487,11 +1429,10 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
else ec->name[0] = (char)0;
ec->status = _status();
ec->type = (_config) ? (_config.isPrivate() ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC) : ZT_NETWORK_TYPE_PRIVATE;
- ec->mtu = ZT_IF_MTU;
- ec->physicalMtu = ZT_UDP_DEFAULT_PAYLOAD_MTU - (ZT_PACKET_IDX_PAYLOAD + 16);
+ ec->mtu = (_config) ? _config.mtu : ZT_DEFAULT_MTU;
ec->dhcp = 0;
std::vector<Address> ab(_config.activeBridges());
- ec->bridge = ((_config.allowPassiveBridging())||(std::find(ab.begin(),ab.end(),RR->identity.address()) != ab.end())) ? 1 : 0;
+ ec->bridge = (std::find(ab.begin(),ab.end(),RR->identity.address()) != ab.end()) ? 1 : 0;
ec->broadcastEnabled = (_config) ? (_config.enableBroadcast() ? 1 : 0) : 0;
ec->portError = _portError;
ec->netconfRevision = (_config) ? (unsigned long)_config.revision : 0;
@@ -1499,7 +1440,7 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
ec->assignedAddressCount = 0;
for(unsigned int i=0;i<ZT_MAX_ZT_ASSIGNED_ADDRESSES;++i) {
if (i < _config.staticIpCount) {
- memcpy(&(ec->assignedAddresses[i]),&(_config.staticIps[i]),sizeof(struct sockaddr_storage));
+ ZT_FAST_MEMCPY(&(ec->assignedAddresses[i]),&(_config.staticIps[i]),sizeof(struct sockaddr_storage));
++ec->assignedAddressCount;
} else {
memset(&(ec->assignedAddresses[i]),0,sizeof(struct sockaddr_storage));
@@ -1509,7 +1450,7 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
ec->routeCount = 0;
for(unsigned int i=0;i<ZT_MAX_NETWORK_ROUTES;++i) {
if (i < _config.routeCount) {
- memcpy(&(ec->routes[i]),&(_config.routes[i]),sizeof(ZT_VirtualNetworkRoute));
+ ZT_FAST_MEMCPY(&(ec->routes[i]),&(_config.routes[i]),sizeof(ZT_VirtualNetworkRoute));
++ec->routeCount;
} else {
memset(&(ec->routes[i]),0,sizeof(ZT_VirtualNetworkRoute));
@@ -1520,39 +1461,33 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
void Network::_sendUpdatesToMembers(void *tPtr,const MulticastGroup *const newMulticastGroup)
{
// Assumes _lock is locked
- const uint64_t now = RR->node->now();
+ const int64_t now = RR->node->now();
std::vector<MulticastGroup> groups;
if (newMulticastGroup)
groups.push_back(*newMulticastGroup);
else groups = _allMulticastGroups();
+ std::vector<Address> alwaysAnnounceTo;
+
if ((newMulticastGroup)||((now - _lastAnnouncedMulticastGroupsUpstream) >= ZT_MULTICAST_ANNOUNCE_PERIOD)) {
if (!newMulticastGroup)
_lastAnnouncedMulticastGroupsUpstream = now;
- // Announce multicast groups to upstream peers (roots, etc.) and also send
- // them our COM so that MULTICAST_GATHER can be authenticated properly.
+ alwaysAnnounceTo = _config.alwaysContactAddresses();
+ if (std::find(alwaysAnnounceTo.begin(),alwaysAnnounceTo.end(),controller()) == alwaysAnnounceTo.end())
+ alwaysAnnounceTo.push_back(controller());
const std::vector<Address> upstreams(RR->topology->upstreamAddresses());
for(std::vector<Address>::const_iterator a(upstreams.begin());a!=upstreams.end();++a) {
- if (_config.com) {
- Packet outp(*a,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
- _config.com.serialize(outp);
- outp.append((uint8_t)0x00);
- outp.append((uint16_t)0); // no capabilities
- outp.append((uint16_t)0); // no tags
- outp.append((uint16_t)0); // no revocations
- outp.append((uint16_t)0); // no certificates of ownership
- RR->sw->send(tPtr,outp,true);
- }
- _announceMulticastGroupsTo(tPtr,*a,groups);
+ if (std::find(alwaysAnnounceTo.begin(),alwaysAnnounceTo.end(),*a) == alwaysAnnounceTo.end())
+ alwaysAnnounceTo.push_back(*a);
}
+ std::sort(alwaysAnnounceTo.begin(),alwaysAnnounceTo.end());
- // Also announce to controller, and send COM to simplify and generalize behavior even though in theory it does not need it
- const Address c(controller());
- if ( (std::find(upstreams.begin(),upstreams.end(),c) == upstreams.end()) && (!_memberships.contains(c)) ) {
- if (_config.com) {
- Packet outp(c,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
+ for(std::vector<Address>::const_iterator a(alwaysAnnounceTo.begin());a!=alwaysAnnounceTo.end();++a) {
+ // push COM to non-members so they can do multicast request auth
+ if ( (_config.com) && (!_memberships.contains(*a)) && (*a != RR->identity.address()) ) {
+ Packet outp(*a,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
_config.com.serialize(outp);
outp.append((uint8_t)0x00);
outp.append((uint16_t)0); // no capabilities
@@ -1561,24 +1496,17 @@ void Network::_sendUpdatesToMembers(void *tPtr,const MulticastGroup *const newMu
outp.append((uint16_t)0); // no certificates of ownership
RR->sw->send(tPtr,outp,true);
}
- _announceMulticastGroupsTo(tPtr,c,groups);
+ _announceMulticastGroupsTo(tPtr,*a,groups);
}
}
- // Make sure that all "network anchors" have Membership records so we will
- // push multicasts to them.
- const std::vector<Address> anchors(_config.anchors());
- for(std::vector<Address>::const_iterator a(anchors.begin());a!=anchors.end();++a)
- _membership(*a);
-
- // Send credentials and multicast LIKEs to members, upstreams, and controller
{
Address *a = (Address *)0;
Membership *m = (Membership *)0;
Hashtable<Address,Membership>::Iterator i(_memberships);
while (i.next(a,m)) {
m->pushCredentials(RR,tPtr,now,*a,_config,-1,false);
- if ( ( m->multicastLikeGate(now) || (newMulticastGroup) ) && (m->isAllowedOnNetwork(_config)) )
+ if ( ( m->multicastLikeGate(now) || (newMulticastGroup) ) && (m->isAllowedOnNetwork(_config)) && (!std::binary_search(alwaysAnnounceTo.begin(),alwaysAnnounceTo.end(),*a)) )
_announceMulticastGroupsTo(tPtr,*a,groups);
}
}
diff --git a/node/Network.hpp b/node/Network.hpp
index faef0fed..95b5483a 100644
--- a/node/Network.hpp
+++ b/node/Network.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_NETWORK_HPP
@@ -30,7 +38,6 @@
#include <stdexcept>
#include "Constants.hpp"
-#include "NonCopyable.hpp"
#include "Hashtable.hpp"
#include "Address.hpp"
#include "Mutex.hpp"
@@ -55,7 +62,7 @@ class Peer;
/**
* A virtual LAN
*/
-class Network : NonCopyable
+class Network
{
friend class SharedPtr<Network>;
@@ -68,7 +75,7 @@ public:
/**
* Compute primary controller device ID from network ID
*/
- static inline Address controllerFor(uint64_t nwid) throw() { return Address(nwid >> 24); }
+ static inline Address controllerFor(uint64_t nwid) { return Address(nwid >> 24); }
/**
* Construct a new network
@@ -80,8 +87,9 @@ public:
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param nwid Network ID
* @param uptr Arbitrary pointer used by externally-facing API (for user use)
+ * @param nconf Network config, if known
*/
- Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *uptr);
+ Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *uptr,const NetworkConfig *nconf);
~Network();
@@ -89,7 +97,7 @@ public:
inline Address controller() const { return Address(_id >> 24); }
inline bool multicastEnabled() const { return (_config.multicastLimit > 0); }
inline bool hasConfig() const { return (_config); }
- inline uint64_t lastConfigUpdate() const throw() { return _lastConfigUpdate; }
+ inline uint64_t lastConfigUpdate() const { return _lastConfigUpdate; }
inline ZT_VirtualNetworkStatus status() const { Mutex::Lock _l(_lock); return _status(); }
inline const NetworkConfig &config() const { return _config; }
inline const MAC &mac() const { return _mac; }
@@ -240,6 +248,19 @@ public:
bool gate(void *tPtr,const SharedPtr<Peer> &peer);
/**
+ * Check whether a given peer has recently had an association with this network
+ *
+ * This checks whether a peer has communicated with us recently about this
+ * network and has possessed a valid certificate of membership. This may return
+ * true even if the peer has been offline for a while or no longer has a valid
+ * certificate of membership but had one recently.
+ *
+ * @param addr Peer address
+ * @return True if peer has recently associated
+ */
+ bool recentlyAssociatedWith(const Address &addr);
+
+ /**
* Do periodic cleanup and housekeeping tasks
*/
void clean();
@@ -283,7 +304,7 @@ public:
* @param mg Multicast group
* @param now Current time
*/
- void learnBridgedMulticastGroup(void *tPtr,const MulticastGroup &mg,uint64_t now);
+ void learnBridgedMulticastGroup(void *tPtr,const MulticastGroup &mg,int64_t now);
/**
* Validate a credential and learn it if it passes certificate and other checks
@@ -335,7 +356,7 @@ public:
* @param to Destination peer address
* @param now Current time
*/
- inline void pushCredentialsNow(void *tPtr,const Address &to,const uint64_t now)
+ inline void pushCredentialsNow(void *tPtr,const Address &to,const int64_t now)
{
Mutex::Lock _l(_lock);
_membership(to).pushCredentials(RR,tPtr,now,to,_config,-1,true);
diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp
index fe7393e8..db051699 100644
--- a/node/NetworkConfig.cpp
+++ b/node/NetworkConfig.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdint.h>
@@ -27,6 +35,7 @@ namespace ZeroTier {
bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,bool includeLegacy) const
{
Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> *tmp = new Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY>();
+ char tmp2[128];
try {
d.clear();
@@ -38,15 +47,17 @@ bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,b
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,this->timestamp)) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA,this->credentialTimeMaxDelta)) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_REVISION,this->revision)) return false;
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,this->issuedTo)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,this->issuedTo.toString(tmp2))) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_TARGET,this->remoteTraceTarget.toString(tmp2))) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_LEVEL,(uint64_t)this->remoteTraceLevel)) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,this->flags)) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,(uint64_t)this->multicastLimit)) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint64_t)this->type)) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_MTU,(uint64_t)this->mtu)) return false;
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
if (includeLegacy) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING_OLD,this->allowPassiveBridging())) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD,this->enableBroadcast())) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_PRIVATE_OLD,this->isPrivate())) return false;
@@ -55,7 +66,8 @@ bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,b
if (this->staticIps[i].ss_family == AF_INET) {
if (v4s.length() > 0)
v4s.push_back(',');
- v4s.append(this->staticIps[i].toString());
+ char buf[64];
+ v4s.append(this->staticIps[i].toString(buf));
}
}
if (v4s.length() > 0) {
@@ -66,7 +78,8 @@ bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,b
if (this->staticIps[i].ss_family == AF_INET6) {
if (v6s.length() > 0)
v6s.push_back(',');
- v6s.append(this->staticIps[i].toString());
+ char buf[64];
+ v6s.append(this->staticIps[i].toString(buf));
}
}
if (v6s.length() > 0) {
@@ -85,8 +98,7 @@ bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,b
if (ets.length() > 0)
ets.push_back(',');
char tmp2[16];
- Utils::snprintf(tmp2,sizeof(tmp2),"%x",et);
- ets.append(tmp2);
+ ets.append(Utils::hex((uint16_t)et,tmp2));
}
et = 0;
}
@@ -105,7 +117,8 @@ bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,b
if ((this->specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0) {
if (ab.length() > 0)
ab.push_back(',');
- ab.append(Address(this->specialists[i]).toString().c_str());
+ char tmp2[16];
+ ab.append(Address(this->specialists[i]).toString(tmp2));
}
}
if (ab.length() > 0) {
@@ -206,16 +219,22 @@ bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACI
delete tmp;
return false;
}
+ this->remoteTraceTarget = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_TARGET);
+ this->remoteTraceLevel = (Trace::Level)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_LEVEL);
this->multicastLimit = (unsigned int)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,0);
d.get(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name,sizeof(this->name));
+ this->mtu = (unsigned int)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_MTU,ZT_DEFAULT_MTU);
+ if (this->mtu < 1280)
+ this->mtu = 1280; // minimum MTU allowed by IPv6 standard and others
+ else if (this->mtu > ZT_MAX_MTU)
+ this->mtu = ZT_MAX_MTU;
+
if (d.getUI(ZT_NETWORKCONFIG_DICT_KEY_VERSION,0) < 6) {
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
char tmp2[1024];
// Decode legacy fields if version is old
- if (d.getB(ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING_OLD))
- this->flags |= ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING;
if (d.getB(ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD))
this->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST;
this->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION; // always enable for old-style netconf
diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp
index 85c24090..44066c86 100644
--- a/node/NetworkConfig.hpp
+++ b/node/NetworkConfig.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_NETWORKCONFIG_HPP
@@ -39,8 +47,10 @@
#include "Capability.hpp"
#include "Tag.hpp"
#include "Dictionary.hpp"
+#include "Hashtable.hpp"
#include "Identity.hpp"
#include "Utils.hpp"
+#include "Trace.hpp"
/**
* Default maximum time delta for COMs, tags, and capabilities
@@ -59,11 +69,6 @@
#define ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA 185000ULL
/**
- * Flag: allow passive bridging (experimental)
- */
-#define ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING 0x0000000000000001ULL
-
-/**
* Flag: enable broadcast
*/
#define ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST 0x0000000000000002ULL
@@ -84,19 +89,19 @@
#define ZT_NETWORKCONFIG_FLAG_DISABLE_COMPRESSION 0x0000000000000010ULL
/**
- * Device is an active bridge
+ * Device can bridge to other Ethernet networks and gets unknown recipient multicasts
*/
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE 0x0000020000000000ULL
/**
- * Anchors are stable devices on this network that can cache multicast info, etc.
+ * Anchors are stable devices on this network that can act like roots when none are up
*/
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR 0x0000040000000000ULL
/**
- * Device can send CIRCUIT_TESTs for this network
+ * Designated multicast replicators replicate multicast in place of sender-side replication
*/
-#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_CIRCUIT_TESTER 0x0000080000000000ULL
+#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR 0x0000080000000000ULL
namespace ZeroTier {
@@ -151,6 +156,10 @@ namespace ZeroTier {
#define ZT_NETWORKCONFIG_DICT_KEY_REVISION "r"
// address of member
#define ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO "id"
+// remote trace target
+#define ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_TARGET "tt"
+// remote trace level
+#define ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_LEVEL "tl"
// flags(hex)
#define ZT_NETWORKCONFIG_DICT_KEY_FLAGS "f"
// integer(hex)
@@ -159,6 +168,8 @@ namespace ZeroTier {
#define ZT_NETWORKCONFIG_DICT_KEY_TYPE "t"
// text
#define ZT_NETWORKCONFIG_DICT_KEY_NAME "n"
+// network MTU
+#define ZT_NETWORKCONFIG_DICT_KEY_MTU "mtu"
// credential time max delta in ms
#define ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA "ctmd"
// binary serialized certificate of membership
@@ -177,14 +188,10 @@ namespace ZeroTier {
#define ZT_NETWORKCONFIG_DICT_KEY_TAGS "TAG"
// tags (binary blobs)
#define ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP "COO"
-// curve25519 signature
-#define ZT_NETWORKCONFIG_DICT_KEY_SIGNATURE "C25519"
// Legacy fields -- these are obsoleted but are included when older clients query
// boolean (now a flag)
-#define ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING_OLD "pb"
-// boolean (now a flag)
#define ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD "eb"
// IP/bits[,IP/bits,...]
// Note that IPs that end in all zeroes are routes with no assignment in them.
@@ -214,21 +221,9 @@ namespace ZeroTier {
class NetworkConfig
{
public:
- NetworkConfig()
- {
- memset(this,0,sizeof(NetworkConfig));
- }
-
- NetworkConfig(const NetworkConfig &nc)
- {
- memcpy(this,&nc,sizeof(NetworkConfig));
- }
-
- inline NetworkConfig &operator=(const NetworkConfig &nc)
- {
- memcpy(this,&nc,sizeof(NetworkConfig));
- return *this;
- }
+ NetworkConfig() { memset(this,0,sizeof(NetworkConfig)); }
+ NetworkConfig(const NetworkConfig &nc) { ZT_FAST_MEMCPY(this,&nc,sizeof(NetworkConfig)); }
+ inline NetworkConfig &operator=(const NetworkConfig &nc) { ZT_FAST_MEMCPY(this,&nc,sizeof(NetworkConfig)); return *this; }
/**
* Write this network config to a dictionary for transport
@@ -248,34 +243,29 @@ public:
bool fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d);
/**
- * @return True if passive bridging is allowed (experimental)
- */
- inline bool allowPassiveBridging() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING) != 0); }
-
- /**
* @return True if broadcast (ff:ff:ff:ff:ff:ff) address should work on this network
*/
- inline bool enableBroadcast() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST) != 0); }
+ inline bool enableBroadcast() const { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST) != 0); }
/**
* @return True if IPv6 NDP emulation should be allowed for certain "magic" IPv6 address patterns
*/
- inline bool ndpEmulation() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION) != 0); }
+ inline bool ndpEmulation() const { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION) != 0); }
/**
* @return True if frames should not be compressed
*/
- inline bool disableCompression() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_DISABLE_COMPRESSION) != 0); }
+ inline bool disableCompression() const { return ((this->flags & ZT_NETWORKCONFIG_FLAG_DISABLE_COMPRESSION) != 0); }
/**
* @return Network type is public (no access control)
*/
- inline bool isPublic() const throw() { return (this->type == ZT_NETWORK_TYPE_PUBLIC); }
+ inline bool isPublic() const { return (this->type == ZT_NETWORK_TYPE_PUBLIC); }
/**
* @return Network type is private (certificate access control)
*/
- inline bool isPrivate() const throw() { return (this->type == ZT_NETWORK_TYPE_PRIVATE); }
+ inline bool isPrivate() const { return (this->type == ZT_NETWORK_TYPE_PRIVATE); }
/**
* @return ZeroTier addresses of devices on this network designated as active bridges
@@ -290,9 +280,25 @@ public:
return r;
}
- /**
- * @return ZeroTier addresses of "anchor" devices on this network
- */
+ inline unsigned int activeBridges(Address ab[ZT_MAX_NETWORK_SPECIALISTS]) const
+ {
+ unsigned int c = 0;
+ for(unsigned int i=0;i<specialistCount;++i) {
+ if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0)
+ ab[c++] = specialists[i];
+ }
+ return c;
+ }
+
+ inline bool isActiveBridge(const Address &a) const
+ {
+ for(unsigned int i=0;i<specialistCount;++i) {
+ if (((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0)&&(a == specialists[i]))
+ return true;
+ }
+ return false;
+ }
+
inline std::vector<Address> anchors() const
{
std::vector<Address> r;
@@ -303,54 +309,78 @@ public:
return r;
}
- /**
- * @param a Address to check
- * @return True if address is an anchor
- */
- inline bool isAnchor(const Address &a) const
+ inline std::vector<Address> multicastReplicators() const
{
+ std::vector<Address> r;
for(unsigned int i=0;i<specialistCount;++i) {
- if ((a == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR) != 0))
- return true;
+ if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR) != 0)
+ r.push_back(Address(specialists[i]));
}
- return false;
+ return r;
}
- /**
- * @param fromPeer Peer attempting to bridge other Ethernet peers onto network
- * @return True if this network allows bridging
- */
- inline bool permitsBridging(const Address &fromPeer) const
+ inline unsigned int multicastReplicators(Address mr[ZT_MAX_NETWORK_SPECIALISTS]) const
{
- if ((flags & ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING) != 0)
- return true;
+ unsigned int c = 0;
for(unsigned int i=0;i<specialistCount;++i) {
- if ((fromPeer == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0))
+ if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR) != 0)
+ mr[c++] = specialists[i];
+ }
+ return c;
+ }
+
+ inline bool isMulticastReplicator(const Address &a) const
+ {
+ for(unsigned int i=0;i<specialistCount;++i) {
+ if (((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR) != 0)&&(a == specialists[i]))
return true;
}
return false;
}
+ inline std::vector<Address> alwaysContactAddresses() const
+ {
+ std::vector<Address> r;
+ for(unsigned int i=0;i<specialistCount;++i) {
+ if ((specialists[i] & (ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR | ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR)) != 0)
+ r.push_back(Address(specialists[i]));
+ }
+ return r;
+ }
+
+ inline unsigned int alwaysContactAddresses(Address ac[ZT_MAX_NETWORK_SPECIALISTS]) const
+ {
+ unsigned int c = 0;
+ for(unsigned int i=0;i<specialistCount;++i) {
+ if ((specialists[i] & (ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR | ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR)) != 0)
+ ac[c++] = specialists[i];
+ }
+ return c;
+ }
+
+ inline void alwaysContactAddresses(Hashtable< Address,std::vector<InetAddress> > &a) const
+ {
+ for(unsigned int i=0;i<specialistCount;++i) {
+ if ((specialists[i] & (ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR | ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR)) != 0) {
+ a[Address(specialists[i])];
+ }
+ }
+ }
+
/**
- * @param byPeer Address to check
- * @return True if this peer is allowed to do circuit tests on this network (controller is always true)
+ * @param fromPeer Peer attempting to bridge other Ethernet peers onto network
+ * @return True if this network allows bridging
*/
- inline bool circuitTestingAllowed(const Address &byPeer) const
+ inline bool permitsBridging(const Address &fromPeer) const
{
- if (byPeer.toInt() == ((networkId >> 24) & 0xffffffffffULL))
- return true;
for(unsigned int i=0;i<specialistCount;++i) {
- if ((byPeer == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_CIRCUIT_TESTER) != 0))
+ if ((fromPeer == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0))
return true;
}
return false;
}
- /**
- * @return True if this network config is non-NULL
- */
- inline operator bool() const throw() { return (networkId != 0); }
-
+ inline operator bool() const { return (networkId != 0); }
inline bool operator==(const NetworkConfig &nc) const { return (memcmp(this,&nc,sizeof(NetworkConfig)) == 0); }
inline bool operator!=(const NetworkConfig &nc) const { return (!(*this == nc)); }
@@ -398,35 +428,6 @@ public:
return (Tag *)0;
}
- /*
- inline void dump() const
- {
- printf("networkId==%.16llx\n",networkId);
- printf("timestamp==%llu\n",timestamp);
- printf("credentialTimeMaxDelta==%llu\n",credentialTimeMaxDelta);
- printf("revision==%llu\n",revision);
- printf("issuedTo==%.10llx\n",issuedTo.toInt());
- printf("multicastLimit==%u\n",multicastLimit);
- printf("flags=%.8lx\n",(unsigned long)flags);
- printf("specialistCount==%u\n",specialistCount);
- for(unsigned int i=0;i<specialistCount;++i)
- printf(" specialists[%u]==%.16llx\n",i,specialists[i]);
- printf("routeCount==%u\n",routeCount);
- for(unsigned int i=0;i<routeCount;++i) {
- printf(" routes[i].target==%s\n",reinterpret_cast<const InetAddress *>(&(routes[i].target))->toString().c_str());
- printf(" routes[i].via==%s\n",reinterpret_cast<const InetAddress *>(&(routes[i].via))->toIpString().c_str());
- printf(" routes[i].flags==%.4x\n",(unsigned int)routes[i].flags);
- printf(" routes[i].metric==%u\n",(unsigned int)routes[i].metric);
- }
- printf("staticIpCount==%u\n",staticIpCount);
- for(unsigned int i=0;i<staticIpCount;++i)
- printf(" staticIps[i]==%s\n",staticIps[i].toString().c_str());
- printf("ruleCount==%u\n",ruleCount);
- printf("name==%s\n",name);
- printf("com==%s\n",com.toString().c_str());
- }
- */
-
/**
* Network ID that this configuration applies to
*/
@@ -435,12 +436,12 @@ public:
/**
* Controller-side time of config generation/issue
*/
- uint64_t timestamp;
+ int64_t timestamp;
/**
* Max difference between timestamp and tag/capability timestamp
*/
- uint64_t credentialTimeMaxDelta;
+ int64_t credentialTimeMaxDelta;
/**
* Controller-side revision counter for this configuration
@@ -453,11 +454,26 @@ public:
Address issuedTo;
/**
+ * If non-NULL, remote traces related to this network are sent here
+ */
+ Address remoteTraceTarget;
+
+ /**
* Flags (64-bit)
*/
uint64_t flags;
/**
+ * Remote trace level
+ */
+ Trace::Level remoteTraceLevel;
+
+ /**
+ * Network MTU
+ */
+ unsigned int mtu;
+
+ /**
* Maximum number of recipients per multicast (not including active bridges)
*/
unsigned int multicastLimit;
diff --git a/node/NetworkController.hpp b/node/NetworkController.hpp
index 0634f435..393bcc91 100644
--- a/node/NetworkController.hpp
+++ b/node/NetworkController.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_NETWORKCONFIGMASTER_HPP
diff --git a/node/Node.cpp b/node/Node.cpp
index 2b3f7996..db511430 100644
--- a/node/Node.cpp
+++ b/node/Node.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -25,6 +33,7 @@
#include "../version.h"
#include "Constants.hpp"
+#include "SharedPtr.hpp"
#include "Node.hpp"
#include "RuntimeEnvironment.hpp"
#include "NetworkController.hpp"
@@ -36,9 +45,8 @@
#include "Address.hpp"
#include "Identity.hpp"
#include "SelfAwareness.hpp"
-#include "Cluster.hpp"
-
-const struct sockaddr_storage ZT_SOCKADDR_NULL = {0};
+#include "Network.hpp"
+#include "Trace.hpp"
namespace ZeroTier {
@@ -46,18 +54,21 @@ namespace ZeroTier {
/* Public Node interface (C++, exposed via CAPI bindings) */
/****************************************************************************/
-Node::Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now) :
+Node::Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64_t now) :
_RR(this),
RR(&_RR),
_uPtr(uptr),
+ _networks(8),
_now(now),
_lastPingCheck(0),
- _lastHousekeepingRun(0)
+ _lastHousekeepingRun(0),
+ _lastMemoizedTraceSettings(0)
{
if (callbacks->version != 0)
- throw std::runtime_error("callbacks struct version mismatch");
- memcpy(&_cb,callbacks,sizeof(ZT_Node_Callbacks));
+ throw ZT_EXCEPTION_INVALID_ARGUMENT;
+ ZT_FAST_MEMCPY(&_cb,callbacks,sizeof(ZT_Node_Callbacks));
+ // Initialize non-cryptographic PRNG from a good random source
Utils::getSecureRandom((void *)_prngState,sizeof(_prngState));
_online = false;
@@ -66,32 +77,66 @@ Node::Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,uint6
memset(_expectingRepliesTo,0,sizeof(_expectingRepliesTo));
memset(_lastIdentityVerification,0,sizeof(_lastIdentityVerification));
- std::string idtmp(dataStoreGet(tptr,"identity.secret"));
- if ((!idtmp.length())||(!RR->identity.fromString(idtmp))||(!RR->identity.hasPrivate())) {
- TRACE("identity.secret not found, generating...");
- RR->identity.generate();
- idtmp = RR->identity.toString(true);
- if (!dataStorePut(tptr,"identity.secret",idtmp,true))
- throw std::runtime_error("unable to write identity.secret");
+ uint64_t idtmp[2];
+ idtmp[0] = 0; idtmp[1] = 0;
+ char tmp[2048];
+ int n = stateObjectGet(tptr,ZT_STATE_OBJECT_IDENTITY_SECRET,idtmp,tmp,sizeof(tmp) - 1);
+ if (n > 0) {
+ tmp[n] = (char)0;
+ if (RR->identity.fromString(tmp)) {
+ RR->identity.toString(false,RR->publicIdentityStr);
+ RR->identity.toString(true,RR->secretIdentityStr);
+ } else {
+ n = -1;
+ }
}
- RR->publicIdentityStr = RR->identity.toString(false);
- RR->secretIdentityStr = RR->identity.toString(true);
- idtmp = dataStoreGet(tptr,"identity.public");
- if (idtmp != RR->publicIdentityStr) {
- if (!dataStorePut(tptr,"identity.public",RR->publicIdentityStr,false))
- throw std::runtime_error("unable to write identity.public");
+
+ if (n <= 0) {
+ RR->identity.generate();
+ RR->identity.toString(false,RR->publicIdentityStr);
+ RR->identity.toString(true,RR->secretIdentityStr);
+ idtmp[0] = RR->identity.address().toInt(); idtmp[1] = 0;
+ stateObjectPut(tptr,ZT_STATE_OBJECT_IDENTITY_SECRET,idtmp,RR->secretIdentityStr,(unsigned int)strlen(RR->secretIdentityStr));
+ stateObjectPut(tptr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,idtmp,RR->publicIdentityStr,(unsigned int)strlen(RR->publicIdentityStr));
+ } else {
+ idtmp[0] = RR->identity.address().toInt(); idtmp[1] = 0;
+ n = stateObjectGet(tptr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,idtmp,tmp,sizeof(tmp) - 1);
+ if ((n > 0)&&(n < (int)sizeof(RR->publicIdentityStr))&&(n < (int)sizeof(tmp))) {
+ if (memcmp(tmp,RR->publicIdentityStr,n))
+ stateObjectPut(tptr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,idtmp,RR->publicIdentityStr,(unsigned int)strlen(RR->publicIdentityStr));
+ }
}
+ char *m = (char *)0;
try {
- RR->sw = new Switch(RR);
- RR->mc = new Multicaster(RR);
- RR->topology = new Topology(RR,tptr);
- RR->sa = new SelfAwareness(RR);
+ const unsigned long ts = sizeof(Trace) + (((sizeof(Trace) & 0xf) != 0) ? (16 - (sizeof(Trace) & 0xf)) : 0);
+ const unsigned long sws = sizeof(Switch) + (((sizeof(Switch) & 0xf) != 0) ? (16 - (sizeof(Switch) & 0xf)) : 0);
+ const unsigned long mcs = sizeof(Multicaster) + (((sizeof(Multicaster) & 0xf) != 0) ? (16 - (sizeof(Multicaster) & 0xf)) : 0);
+ const unsigned long topologys = sizeof(Topology) + (((sizeof(Topology) & 0xf) != 0) ? (16 - (sizeof(Topology) & 0xf)) : 0);
+ const unsigned long sas = sizeof(SelfAwareness) + (((sizeof(SelfAwareness) & 0xf) != 0) ? (16 - (sizeof(SelfAwareness) & 0xf)) : 0);
+
+ m = reinterpret_cast<char *>(::malloc(16 + ts + sws + mcs + topologys + sas));
+ if (!m)
+ throw std::bad_alloc();
+ RR->rtmem = m;
+ while (((uintptr_t)m & 0xf) != 0) ++m;
+
+ RR->t = new (m) Trace(RR);
+ m += ts;
+ RR->sw = new (m) Switch(RR);
+ m += sws;
+ RR->mc = new (m) Multicaster(RR);
+ m += mcs;
+ RR->topology = new (m) Topology(RR,tptr);
+ m += topologys;
+ RR->sa = new (m) SelfAwareness(RR);
} catch ( ... ) {
- delete RR->sa;
- delete RR->topology;
- delete RR->mc;
- delete RR->sw;
+ if (RR->sa) RR->sa->~SelfAwareness();
+ if (RR->topology) RR->topology->~Topology();
+ if (RR->mc) RR->mc->~Multicaster();
+ if (RR->sw) RR->sw->~Switch();
+ if (RR->t) RR->t->~Trace();
+ ::free(m);
throw;
}
@@ -100,37 +145,35 @@ Node::Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,uint6
Node::~Node()
{
- Mutex::Lock _l(_networks_m);
-
- _networks.clear(); // ensure that networks are destroyed before shutdow
-
- delete RR->sa;
- delete RR->topology;
- delete RR->mc;
- delete RR->sw;
-
-#ifdef ZT_ENABLE_CLUSTER
- delete RR->cluster;
-#endif
+ {
+ Mutex::Lock _l(_networks_m);
+ _networks.clear(); // destroy all networks before shutdown
+ }
+ if (RR->sa) RR->sa->~SelfAwareness();
+ if (RR->topology) RR->topology->~Topology();
+ if (RR->mc) RR->mc->~Multicaster();
+ if (RR->sw) RR->sw->~Switch();
+ if (RR->t) RR->t->~Trace();
+ ::free(RR->rtmem);
}
ZT_ResultCode Node::processWirePacket(
void *tptr,
- uint64_t now,
- const struct sockaddr_storage *localAddress,
+ int64_t now,
+ int64_t localSocket,
const struct sockaddr_storage *remoteAddress,
const void *packetData,
unsigned int packetLength,
- volatile uint64_t *nextBackgroundTaskDeadline)
+ volatile int64_t *nextBackgroundTaskDeadline)
{
_now = now;
- RR->sw->onRemotePacket(tptr,*(reinterpret_cast<const InetAddress *>(localAddress)),*(reinterpret_cast<const InetAddress *>(remoteAddress)),packetData,packetLength);
+ RR->sw->onRemotePacket(tptr,localSocket,*(reinterpret_cast<const InetAddress *>(remoteAddress)),packetData,packetLength);
return ZT_RESULT_OK;
}
ZT_ResultCode Node::processVirtualNetworkFrame(
void *tptr,
- uint64_t now,
+ int64_t now,
uint64_t nwid,
uint64_t sourceMac,
uint64_t destMac,
@@ -138,7 +181,7 @@ ZT_ResultCode Node::processVirtualNetworkFrame(
unsigned int vlanId,
const void *frameData,
unsigned int frameLength,
- volatile uint64_t *nextBackgroundTaskDeadline)
+ volatile int64_t *nextBackgroundTaskDeadline)
{
_now = now;
SharedPtr<Network> nw(this->network(nwid));
@@ -152,109 +195,129 @@ ZT_ResultCode Node::processVirtualNetworkFrame(
class _PingPeersThatNeedPing
{
public:
- _PingPeersThatNeedPing(const RuntimeEnvironment *renv,void *tPtr,Hashtable< Address,std::vector<InetAddress> > &upstreamsToContact,uint64_t now) :
- lastReceiveFromUpstream(0),
+ _PingPeersThatNeedPing(const RuntimeEnvironment *renv,void *tPtr,Hashtable< Address,std::vector<InetAddress> > &alwaysContact,int64_t now) :
RR(renv),
_tPtr(tPtr),
- _upstreamsToContact(upstreamsToContact),
+ _alwaysContact(alwaysContact),
_now(now),
_bestCurrentUpstream(RR->topology->getUpstreamPeer())
{
}
- uint64_t lastReceiveFromUpstream; // tracks last time we got a packet from an 'upstream' peer like a root or a relay
-
inline void operator()(Topology &t,const SharedPtr<Peer> &p)
{
- const std::vector<InetAddress> *const upstreamStableEndpoints = _upstreamsToContact.get(p->address());
- if (upstreamStableEndpoints) {
- bool contacted = false;
-
- // Upstreams must be pinged constantly over both IPv4 and IPv6 to allow
- // them to perform three way handshake introductions for both stacks.
-
- if (!p->doPingAndKeepalive(_tPtr,_now,AF_INET)) {
- for(unsigned long k=0,ptr=(unsigned long)RR->node->prng();k<(unsigned long)upstreamStableEndpoints->size();++k) {
- const InetAddress &addr = (*upstreamStableEndpoints)[ptr++ % upstreamStableEndpoints->size()];
+ const std::vector<InetAddress> *const alwaysContactEndpoints = _alwaysContact.get(p->address());
+ if (alwaysContactEndpoints) {
+ const unsigned int sent = p->doPingAndKeepalive(_tPtr,_now);
+ bool contacted = (sent != 0);
+
+ if ((sent & 0x1) == 0) { // bit 0x1 == IPv4 sent
+ for(unsigned long k=0,ptr=(unsigned long)RR->node->prng();k<(unsigned long)alwaysContactEndpoints->size();++k) {
+ const InetAddress &addr = (*alwaysContactEndpoints)[ptr++ % alwaysContactEndpoints->size()];
if (addr.ss_family == AF_INET) {
- p->sendHELLO(_tPtr,InetAddress(),addr,_now,0);
+ p->sendHELLO(_tPtr,-1,addr,_now);
contacted = true;
break;
}
}
- } else contacted = true;
- if (!p->doPingAndKeepalive(_tPtr,_now,AF_INET6)) {
- for(unsigned long k=0,ptr=(unsigned long)RR->node->prng();k<(unsigned long)upstreamStableEndpoints->size();++k) {
- const InetAddress &addr = (*upstreamStableEndpoints)[ptr++ % upstreamStableEndpoints->size()];
+ }
+
+ if ((sent & 0x2) == 0) { // bit 0x2 == IPv6 sent
+ for(unsigned long k=0,ptr=(unsigned long)RR->node->prng();k<(unsigned long)alwaysContactEndpoints->size();++k) {
+ const InetAddress &addr = (*alwaysContactEndpoints)[ptr++ % alwaysContactEndpoints->size()];
if (addr.ss_family == AF_INET6) {
- p->sendHELLO(_tPtr,InetAddress(),addr,_now,0);
+ p->sendHELLO(_tPtr,-1,addr,_now);
contacted = true;
break;
}
}
- } else contacted = true;
+ }
if ((!contacted)&&(_bestCurrentUpstream)) {
const SharedPtr<Path> up(_bestCurrentUpstream->getBestPath(_now,true));
if (up)
- p->sendHELLO(_tPtr,up->localAddress(),up->address(),_now,up->nextOutgoingCounter());
+ p->sendHELLO(_tPtr,up->localSocket(),up->address(),_now);
}
- lastReceiveFromUpstream = std::max(p->lastReceive(),lastReceiveFromUpstream);
- _upstreamsToContact.erase(p->address()); // erase from upstreams to contact so that we can WHOIS those that remain
+ _alwaysContact.erase(p->address()); // after this we'll WHOIS all upstreams that remain
} else if (p->isActive(_now)) {
- p->doPingAndKeepalive(_tPtr,_now,-1);
+ p->doPingAndKeepalive(_tPtr,_now);
}
}
private:
const RuntimeEnvironment *RR;
void *_tPtr;
- Hashtable< Address,std::vector<InetAddress> > &_upstreamsToContact;
- const uint64_t _now;
+ Hashtable< Address,std::vector<InetAddress> > &_alwaysContact;
+ const int64_t _now;
const SharedPtr<Peer> _bestCurrentUpstream;
};
-ZT_ResultCode Node::processBackgroundTasks(void *tptr,uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline)
+ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64_t *nextBackgroundTaskDeadline)
{
_now = now;
Mutex::Lock bl(_backgroundTasksLock);
unsigned long timeUntilNextPingCheck = ZT_PING_CHECK_INVERVAL;
- const uint64_t timeSinceLastPingCheck = now - _lastPingCheck;
+ const int64_t timeSinceLastPingCheck = now - _lastPingCheck;
if (timeSinceLastPingCheck >= ZT_PING_CHECK_INVERVAL) {
try {
_lastPingCheck = now;
- // Get networks that need config without leaving mutex locked
- std::vector< SharedPtr<Network> > needConfig;
+ // Get designated VL1 upstreams
+ Hashtable< Address,std::vector<InetAddress> > alwaysContact;
+ RR->topology->getUpstreamsToContact(alwaysContact);
+
+ // Check last receive time on designated upstreams to see if we seem to be online
+ int64_t lastReceivedFromUpstream = 0;
+ {
+ Hashtable< Address,std::vector<InetAddress> >::Iterator i(alwaysContact);
+ Address *upstreamAddress = (Address *)0;
+ std::vector<InetAddress> *upstreamStableEndpoints = (std::vector<InetAddress> *)0;
+ while (i.next(upstreamAddress,upstreamStableEndpoints)) {
+ SharedPtr<Peer> p(RR->topology->getPeerNoCache(*upstreamAddress));
+ if (p)
+ lastReceivedFromUpstream = std::max(p->lastReceive(),lastReceivedFromUpstream);
+ }
+ }
+
+ // Get peers we should stay connected to according to network configs
+ // Also get networks and whether they need config so we only have to do one pass over networks
+ std::vector< std::pair< SharedPtr<Network>,bool > > networkConfigNeeded;
{
- Mutex::Lock _l(_networks_m);
- for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n) {
- if (((now - n->second->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!n->second->hasConfig()))
- needConfig.push_back(n->second);
- n->second->sendUpdatesToMembers(tptr);
+ Mutex::Lock l(_networks_m);
+ Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(_networks);
+ uint64_t *nwid = (uint64_t *)0;
+ SharedPtr<Network> *network = (SharedPtr<Network> *)0;
+ while (i.next(nwid,network)) {
+ (*network)->config().alwaysContactAddresses(alwaysContact);
+ networkConfigNeeded.push_back( std::pair< SharedPtr<Network>,bool >(*network,(((now - (*network)->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!(*network)->hasConfig()))) );
}
}
- for(std::vector< SharedPtr<Network> >::const_iterator n(needConfig.begin());n!=needConfig.end();++n)
- (*n)->requestConfiguration(tptr);
- // Do pings and keepalives
- Hashtable< Address,std::vector<InetAddress> > upstreamsToContact;
- RR->topology->getUpstreamsToContact(upstreamsToContact);
- _PingPeersThatNeedPing pfunc(RR,tptr,upstreamsToContact,now);
+ // Ping active peers, upstreams, and others that we should always contact
+ _PingPeersThatNeedPing pfunc(RR,tptr,alwaysContact,now);
RR->topology->eachPeer<_PingPeersThatNeedPing &>(pfunc);
- // Run WHOIS to create Peer for any upstreams we could not contact (including pending moon seeds)
- Hashtable< Address,std::vector<InetAddress> >::Iterator i(upstreamsToContact);
- Address *upstreamAddress = (Address *)0;
- std::vector<InetAddress> *upstreamStableEndpoints = (std::vector<InetAddress> *)0;
- while (i.next(upstreamAddress,upstreamStableEndpoints))
- RR->sw->requestWhois(tptr,*upstreamAddress);
+ // Run WHOIS to create Peer for alwaysContact addresses that could not be contacted
+ {
+ Hashtable< Address,std::vector<InetAddress> >::Iterator i(alwaysContact);
+ Address *upstreamAddress = (Address *)0;
+ std::vector<InetAddress> *upstreamStableEndpoints = (std::vector<InetAddress> *)0;
+ while (i.next(upstreamAddress,upstreamStableEndpoints))
+ RR->sw->requestWhois(tptr,now,*upstreamAddress);
+ }
+
+ // Refresh network config or broadcast network updates to members as needed
+ for(std::vector< std::pair< SharedPtr<Network>,bool > >::const_iterator n(networkConfigNeeded.begin());n!=networkConfigNeeded.end();++n) {
+ if (n->second)
+ n->first->requestConfiguration(tptr);
+ n->first->sendUpdatesToMembers(tptr);
+ }
// Update online status, post status change as event
const bool oldOnline = _online;
- _online = (((now - pfunc.lastReceiveFromUpstream) < ZT_PEER_ACTIVITY_TIMEOUT)||(RR->topology->amRoot()));
+ _online = (((now - lastReceivedFromUpstream) < ZT_PEER_ACTIVITY_TIMEOUT)||(RR->topology->amUpstream()));
if (oldOnline != _online)
postEvent(tptr,_online ? ZT_EVENT_ONLINE : ZT_EVENT_OFFLINE);
} catch ( ... ) {
@@ -264,10 +327,15 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,uint64_t now,volatile uint
timeUntilNextPingCheck -= (unsigned long)timeSinceLastPingCheck;
}
+ if ((now - _lastMemoizedTraceSettings) >= (ZT_HOUSEKEEPING_PERIOD / 4)) {
+ _lastMemoizedTraceSettings = now;
+ RR->t->updateMemoizedSettings();
+ }
+
if ((now - _lastHousekeepingRun) >= ZT_HOUSEKEEPING_PERIOD) {
+ _lastHousekeepingRun = now;
try {
- _lastHousekeepingRun = now;
- RR->topology->clean(now);
+ RR->topology->doPeriodicTasks(tptr,now);
RR->sa->clean(now);
RR->mc->clean(now);
} catch ( ... ) {
@@ -276,18 +344,7 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,uint64_t now,volatile uint
}
try {
-#ifdef ZT_ENABLE_CLUSTER
- // If clustering is enabled we have to call cluster->doPeriodicTasks() very often, so we override normal timer deadline behavior
- if (RR->cluster) {
- RR->sw->doTimerTasks(tptr,now);
- RR->cluster->doPeriodicTasks();
- *nextBackgroundTaskDeadline = now + ZT_CLUSTER_PERIODIC_TASK_PERIOD; // this is really short so just tick at this rate
- } else {
-#endif
- *nextBackgroundTaskDeadline = now + (uint64_t)std::max(std::min(timeUntilNextPingCheck,RR->sw->doTimerTasks(tptr,now)),(unsigned long)ZT_CORE_TIMER_TASK_GRANULARITY);
-#ifdef ZT_ENABLE_CLUSTER
- }
-#endif
+ *nextBackgroundTaskDeadline = now + (int64_t)std::max(std::min(timeUntilNextPingCheck,RR->sw->doTimerTasks(tptr,now)),(unsigned long)ZT_CORE_TIMER_TASK_GRANULARITY);
} catch ( ... ) {
return ZT_RESULT_FATAL_ERROR_INTERNAL;
}
@@ -298,36 +355,40 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,uint64_t now,volatile uint
ZT_ResultCode Node::join(uint64_t nwid,void *uptr,void *tptr)
{
Mutex::Lock _l(_networks_m);
- SharedPtr<Network> nw = _network(nwid);
- if(!nw) {
- const std::pair< uint64_t,SharedPtr<Network> > nn(nwid,SharedPtr<Network>(new Network(RR,tptr,nwid,uptr)));
- _networks.insert(std::upper_bound(_networks.begin(),_networks.end(),nn),nn);
- }
+ SharedPtr<Network> &nw = _networks[nwid];
+ if (!nw)
+ nw = SharedPtr<Network>(new Network(RR,tptr,nwid,uptr,(const NetworkConfig *)0));
return ZT_RESULT_OK;
}
ZT_ResultCode Node::leave(uint64_t nwid,void **uptr,void *tptr)
{
ZT_VirtualNetworkConfig ctmp;
- std::vector< std::pair< uint64_t,SharedPtr<Network> > > newn;
void **nUserPtr = (void **)0;
- Mutex::Lock _l(_networks_m);
-
- for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n) {
- if (n->first != nwid) {
- newn.push_back(*n);
- } else {
- if (uptr)
- *uptr = *n->second->userPtr();
- n->second->destroy();
- nUserPtr = n->second->userPtr();
- }
+ {
+ Mutex::Lock _l(_networks_m);
+ SharedPtr<Network> *nw = _networks.get(nwid);
+ if (!nw)
+ return ZT_RESULT_OK;
+ if (uptr)
+ *uptr = (*nw)->userPtr();
+ (*nw)->externalConfig(&ctmp);
+ (*nw)->destroy();
+ nUserPtr = (*nw)->userPtr();
}
- _networks.swap(newn);
-
+
if (nUserPtr)
RR->node->configureVirtualNetworkPort(tptr,nwid,nUserPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY,&ctmp);
+ {
+ Mutex::Lock _l(_networks_m);
+ _networks.erase(nwid);
+ }
+
+ uint64_t tmp[2];
+ tmp[0] = nwid; tmp[1] = 0;
+ RR->node->stateObjectDelete(tptr,ZT_STATE_OBJECT_NETWORK_CONFIG,tmp);
+
return ZT_RESULT_OK;
}
@@ -369,8 +430,8 @@ uint64_t Node::address() const
void Node::status(ZT_NodeStatus *status) const
{
status->address = RR->identity.address().toInt();
- status->publicIdentity = RR->publicIdentityStr.c_str();
- status->secretIdentity = RR->secretIdentityStr.c_str();
+ status->publicIdentity = RR->publicIdentityStr;
+ status->secretIdentity = RR->secretIdentityStr;
status->online = _online ? 1 : 0;
}
@@ -398,18 +459,19 @@ ZT_PeerList *Node::peers() const
p->versionMinor = -1;
p->versionRev = -1;
}
- p->latency = pi->second->latency();
+ p->latency = pi->second->latency(_now);
+ if (p->latency >= 0xffff)
+ p->latency = -1;
p->role = RR->topology->role(pi->second->identity().address());
std::vector< SharedPtr<Path> > paths(pi->second->paths(_now));
SharedPtr<Path> bestp(pi->second->getBestPath(_now,false));
p->pathCount = 0;
for(std::vector< SharedPtr<Path> >::iterator path(paths.begin());path!=paths.end();++path) {
- memcpy(&(p->paths[p->pathCount].address),&((*path)->address()),sizeof(struct sockaddr_storage));
+ ZT_FAST_MEMCPY(&(p->paths[p->pathCount].address),&((*path)->address()),sizeof(struct sockaddr_storage));
p->paths[p->pathCount].lastSend = (*path)->lastOut();
p->paths[p->pathCount].lastReceive = (*path)->lastIn();
p->paths[p->pathCount].trustedPathId = RR->topology->getOutboundPathTrust((*path)->address());
- p->paths[p->pathCount].linkQuality = (int)(*path)->linkQuality();
p->paths[p->pathCount].expired = 0;
p->paths[p->pathCount].preferred = ((*path) == bestp) ? 1 : 0;
++p->pathCount;
@@ -422,10 +484,10 @@ ZT_PeerList *Node::peers() const
ZT_VirtualNetworkConfig *Node::networkConfig(uint64_t nwid) const
{
Mutex::Lock _l(_networks_m);
- SharedPtr<Network> nw = _network(nwid);
- if(nw) {
+ const SharedPtr<Network> *nw = _networks.get(nwid);
+ if (nw) {
ZT_VirtualNetworkConfig *nc = (ZT_VirtualNetworkConfig *)::malloc(sizeof(ZT_VirtualNetworkConfig));
- nw->externalConfig(nc);
+ (*nw)->externalConfig(nc);
return nc;
}
return (ZT_VirtualNetworkConfig *)0;
@@ -442,8 +504,11 @@ ZT_VirtualNetworkList *Node::networks() const
nl->networks = (ZT_VirtualNetworkConfig *)(buf + sizeof(ZT_VirtualNetworkList));
nl->networkCount = 0;
- for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n)
- n->second->externalConfig(&(nl->networks[nl->networkCount++]));
+ Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(*const_cast< Hashtable< uint64_t,SharedPtr<Network> > *>(&_networks));
+ uint64_t *k = (uint64_t *)0;
+ SharedPtr<Network> *v = (SharedPtr<Network> *)0;
+ while (i.next(k,v))
+ (*v)->externalConfig(&(nl->networks[nl->networkCount++]));
return nl;
}
@@ -490,154 +555,15 @@ int Node::sendUserMessage(void *tptr,uint64_t dest,uint64_t typeId,const void *d
void Node::setNetconfMaster(void *networkControllerInstance)
{
RR->localNetworkController = reinterpret_cast<NetworkController *>(networkControllerInstance);
- RR->localNetworkController->init(RR->identity,this);
-}
-
-ZT_ResultCode Node::circuitTestBegin(void *tptr,ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *))
-{
- if (test->hopCount > 0) {
- try {
- Packet outp(Address(),RR->identity.address(),Packet::VERB_CIRCUIT_TEST);
- RR->identity.address().appendTo(outp);
- outp.append((uint16_t)((test->reportAtEveryHop != 0) ? 0x03 : 0x02));
- outp.append((uint64_t)test->timestamp);
- outp.append((uint64_t)test->testId);
- outp.append((uint16_t)0); // originator credential length, updated later
- if (test->credentialNetworkId) {
- outp.append((uint8_t)0x01);
- outp.append((uint64_t)test->credentialNetworkId);
- outp.setAt<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 23,(uint16_t)9);
- }
- outp.append((uint16_t)0);
- C25519::Signature sig(RR->identity.sign(reinterpret_cast<const char *>(outp.data()) + ZT_PACKET_IDX_PAYLOAD,outp.size() - ZT_PACKET_IDX_PAYLOAD));
- outp.append((uint16_t)sig.size());
- outp.append(sig.data,(unsigned int)sig.size());
- outp.append((uint16_t)0); // originator doesn't need an extra credential, since it's the originator
- for(unsigned int h=1;h<test->hopCount;++h) {
- outp.append((uint8_t)0);
- outp.append((uint8_t)(test->hops[h].breadth & 0xff));
- for(unsigned int a=0;a<test->hops[h].breadth;++a)
- Address(test->hops[h].addresses[a]).appendTo(outp);
- }
-
- for(unsigned int a=0;a<test->hops[0].breadth;++a) {
- outp.newInitializationVector();
- outp.setDestination(Address(test->hops[0].addresses[a]));
- RR->sw->send(tptr,outp,true);
- }
- } catch ( ... ) {
- return ZT_RESULT_FATAL_ERROR_INTERNAL; // probably indicates FIFO too big for packet
- }
- }
-
- {
- test->_internalPtr = reinterpret_cast<void *>(reportCallback);
- Mutex::Lock _l(_circuitTests_m);
- if (std::find(_circuitTests.begin(),_circuitTests.end(),test) == _circuitTests.end())
- _circuitTests.push_back(test);
- }
-
- return ZT_RESULT_OK;
-}
-
-void Node::circuitTestEnd(ZT_CircuitTest *test)
-{
- Mutex::Lock _l(_circuitTests_m);
- for(;;) {
- std::vector< ZT_CircuitTest * >::iterator ct(std::find(_circuitTests.begin(),_circuitTests.end(),test));
- if (ct == _circuitTests.end())
- break;
- else _circuitTests.erase(ct);
- }
-}
-
-ZT_ResultCode Node::clusterInit(
- unsigned int myId,
- const struct sockaddr_storage *zeroTierPhysicalEndpoints,
- unsigned int numZeroTierPhysicalEndpoints,
- int x,
- int y,
- int z,
- void (*sendFunction)(void *,unsigned int,const void *,unsigned int),
- void *sendFunctionArg,
- int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *),
- void *addressToLocationFunctionArg)
-{
-#ifdef ZT_ENABLE_CLUSTER
- if (RR->cluster)
- return ZT_RESULT_ERROR_BAD_PARAMETER;
-
- std::vector<InetAddress> eps;
- for(unsigned int i=0;i<numZeroTierPhysicalEndpoints;++i)
- eps.push_back(InetAddress(zeroTierPhysicalEndpoints[i]));
- std::sort(eps.begin(),eps.end());
- RR->cluster = new Cluster(RR,myId,eps,x,y,z,sendFunction,sendFunctionArg,addressToLocationFunction,addressToLocationFunctionArg);
-
- return ZT_RESULT_OK;
-#else
- return ZT_RESULT_ERROR_UNSUPPORTED_OPERATION;
-#endif
-}
-
-ZT_ResultCode Node::clusterAddMember(unsigned int memberId)
-{
-#ifdef ZT_ENABLE_CLUSTER
- if (!RR->cluster)
- return ZT_RESULT_ERROR_BAD_PARAMETER;
- RR->cluster->addMember((uint16_t)memberId);
- return ZT_RESULT_OK;
-#else
- return ZT_RESULT_ERROR_UNSUPPORTED_OPERATION;
-#endif
-}
-
-void Node::clusterRemoveMember(unsigned int memberId)
-{
-#ifdef ZT_ENABLE_CLUSTER
- if (RR->cluster)
- RR->cluster->removeMember((uint16_t)memberId);
-#endif
-}
-
-void Node::clusterHandleIncomingMessage(const void *msg,unsigned int len)
-{
-#ifdef ZT_ENABLE_CLUSTER
- if (RR->cluster)
- RR->cluster->handleIncomingStateMessage(msg,len);
-#endif
-}
-
-void Node::clusterStatus(ZT_ClusterStatus *cs)
-{
- if (!cs)
- return;
-#ifdef ZT_ENABLE_CLUSTER
- if (RR->cluster)
- RR->cluster->status(*cs);
- else
-#endif
- memset(cs,0,sizeof(ZT_ClusterStatus));
+ if (networkControllerInstance)
+ RR->localNetworkController->init(RR->identity,this);
}
/****************************************************************************/
/* Node methods used only within node/ */
/****************************************************************************/
-std::string Node::dataStoreGet(void *tPtr,const char *name)
-{
- char buf[1024];
- std::string r;
- unsigned long olen = 0;
- do {
- long n = _cb.dataStoreGetFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,name,buf,sizeof(buf),(unsigned long)r.length(),&olen);
- if (n <= 0)
- return std::string();
- r.append(buf,n);
- } while (r.length() < olen);
- return r;
-}
-
-bool Node::shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,const InetAddress &localAddress,const InetAddress &remoteAddress)
+bool Node::shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,const int64_t localSocket,const InetAddress &remoteAddress)
{
if (!Path::isAddressValidForPath(remoteAddress))
return false;
@@ -647,52 +573,22 @@ bool Node::shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,cons
{
Mutex::Lock _l(_networks_m);
- for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i) {
- if (i->second->hasConfig()) {
- for(unsigned int k=0;k<i->second->config().staticIpCount;++k) {
- if (i->second->config().staticIps[k].containsAddress(remoteAddress))
+ Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(_networks);
+ uint64_t *k = (uint64_t *)0;
+ SharedPtr<Network> *v = (SharedPtr<Network> *)0;
+ while (i.next(k,v)) {
+ if ((*v)->hasConfig()) {
+ for(unsigned int k=0;k<(*v)->config().staticIpCount;++k) {
+ if ((*v)->config().staticIps[k].containsAddress(remoteAddress))
return false;
}
}
}
}
- return ( (_cb.pathCheckFunction) ? (_cb.pathCheckFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,ztaddr.toInt(),reinterpret_cast<const struct sockaddr_storage *>(&localAddress),reinterpret_cast<const struct sockaddr_storage *>(&remoteAddress)) != 0) : true);
+ return ( (_cb.pathCheckFunction) ? (_cb.pathCheckFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,ztaddr.toInt(),localSocket,reinterpret_cast<const struct sockaddr_storage *>(&remoteAddress)) != 0) : true);
}
-#ifdef ZT_TRACE
-void Node::postTrace(const char *module,unsigned int line,const char *fmt,...)
-{
- static Mutex traceLock;
-
- va_list ap;
- char tmp1[1024],tmp2[1024],tmp3[256];
-
- Mutex::Lock _l(traceLock);
-
- time_t now = (time_t)(_now / 1000ULL);
-#ifdef __WINDOWS__
- ctime_s(tmp3,sizeof(tmp3),&now);
- char *nowstr = tmp3;
-#else
- char *nowstr = ctime_r(&now,tmp3);
-#endif
- unsigned long nowstrlen = (unsigned long)strlen(nowstr);
- if (nowstr[nowstrlen-1] == '\n')
- nowstr[--nowstrlen] = (char)0;
- if (nowstr[nowstrlen-1] == '\r')
- nowstr[--nowstrlen] = (char)0;
-
- va_start(ap,fmt);
- vsnprintf(tmp2,sizeof(tmp2),fmt,ap);
- va_end(ap);
- tmp2[sizeof(tmp2)-1] = (char)0;
-
- Utils::snprintf(tmp1,sizeof(tmp1),"[%s] %s:%u %s",nowstr,module,line,tmp2);
- postEvent((void *)0,ZT_EVENT_TRACE,tmp1);
-}
-#endif // ZT_TRACE
-
uint64_t Node::prng()
{
// https://en.wikipedia.org/wiki/Xorshift#xorshift.2B
@@ -705,23 +601,10 @@ uint64_t Node::prng()
return z + y;
}
-void Node::postCircuitTestReport(const ZT_CircuitTestReport *report)
-{
- std::vector< ZT_CircuitTest * > toNotify;
- {
- Mutex::Lock _l(_circuitTests_m);
- for(std::vector< ZT_CircuitTest * >::iterator i(_circuitTests.begin());i!=_circuitTests.end();++i) {
- if ((*i)->testId == report->testId)
- toNotify.push_back(*i);
- }
- }
- for(std::vector< ZT_CircuitTest * >::iterator i(toNotify.begin());i!=toNotify.end();++i)
- (reinterpret_cast<void (*)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)>((*i)->_internalPtr))(reinterpret_cast<ZT_Node *>(this),*i,report);
-}
-
-void Node::setTrustedPaths(const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count)
+ZT_ResultCode Node::setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork, const ZT_PhysicalPathConfiguration *pathConfig)
{
- RR->topology->setTrustedPaths(reinterpret_cast<const InetAddress *>(networks),ids,count);
+ RR->topology->setPhysicalPathConfiguration(pathNetwork,pathConfig);
+ return ZT_RESULT_OK;
}
World Node::planet() const
@@ -750,7 +633,7 @@ void Node::ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &de
const unsigned int totalSize = dconf->sizeBytes();
unsigned int chunkIndex = 0;
while (chunkIndex < totalSize) {
- const unsigned int chunkLen = std::min(totalSize - chunkIndex,(unsigned int)(ZT_UDP_DEFAULT_PAYLOAD_MTU - (ZT_PACKET_IDX_PAYLOAD + 256)));
+ const unsigned int chunkLen = std::min(totalSize - chunkIndex,(unsigned int)(ZT_PROTO_MAX_PACKET_LENGTH - (ZT_PACKET_IDX_PAYLOAD + 256)));
Packet outp(destination,RR->identity.address(),(requestPacketId) ? Packet::VERB_OK : Packet::VERB_NETWORK_CONFIG);
if (requestPacketId) {
outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
@@ -846,7 +729,7 @@ void Node::ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &des
extern "C" {
-enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now)
+enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64_t now)
{
*node = (ZT_Node *)0;
try {
@@ -871,15 +754,15 @@ void ZT_Node_delete(ZT_Node *node)
enum ZT_ResultCode ZT_Node_processWirePacket(
ZT_Node *node,
void *tptr,
- uint64_t now,
- const struct sockaddr_storage *localAddress,
+ int64_t now,
+ int64_t localSocket,
const struct sockaddr_storage *remoteAddress,
const void *packetData,
unsigned int packetLength,
- volatile uint64_t *nextBackgroundTaskDeadline)
+ volatile int64_t *nextBackgroundTaskDeadline)
{
try {
- return reinterpret_cast<ZeroTier::Node *>(node)->processWirePacket(tptr,now,localAddress,remoteAddress,packetData,packetLength,nextBackgroundTaskDeadline);
+ return reinterpret_cast<ZeroTier::Node *>(node)->processWirePacket(tptr,now,localSocket,remoteAddress,packetData,packetLength,nextBackgroundTaskDeadline);
} catch (std::bad_alloc &exc) {
return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
} catch ( ... ) {
@@ -890,7 +773,7 @@ enum ZT_ResultCode ZT_Node_processWirePacket(
enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame(
ZT_Node *node,
void *tptr,
- uint64_t now,
+ int64_t now,
uint64_t nwid,
uint64_t sourceMac,
uint64_t destMac,
@@ -898,7 +781,7 @@ enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame(
unsigned int vlanId,
const void *frameData,
unsigned int frameLength,
- volatile uint64_t *nextBackgroundTaskDeadline)
+ volatile int64_t *nextBackgroundTaskDeadline)
{
try {
return reinterpret_cast<ZeroTier::Node *>(node)->processVirtualNetworkFrame(tptr,now,nwid,sourceMac,destMac,etherType,vlanId,frameData,frameLength,nextBackgroundTaskDeadline);
@@ -909,7 +792,7 @@ enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame(
}
}
-enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,void *tptr,uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline)
+enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,void *tptr,int64_t now,volatile int64_t *nextBackgroundTaskDeadline)
{
try {
return reinterpret_cast<ZeroTier::Node *>(node)->processBackgroundTasks(tptr,now,nextBackgroundTaskDeadline);
@@ -973,7 +856,7 @@ enum ZT_ResultCode ZT_Node_orbit(ZT_Node *node,void *tptr,uint64_t moonWorldId,u
}
}
-ZT_ResultCode ZT_Node_deorbit(ZT_Node *node,void *tptr,uint64_t moonWorldId)
+enum ZT_ResultCode ZT_Node_deorbit(ZT_Node *node,void *tptr,uint64_t moonWorldId)
{
try {
return reinterpret_cast<ZeroTier::Node *>(node)->deorbit(tptr,moonWorldId);
@@ -1060,79 +943,15 @@ void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkControllerInstance)
} catch ( ... ) {}
}
-enum ZT_ResultCode ZT_Node_circuitTestBegin(ZT_Node *node,void *tptr,ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *))
+enum ZT_ResultCode ZT_Node_setPhysicalPathConfiguration(ZT_Node *node,const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig)
{
try {
- return reinterpret_cast<ZeroTier::Node *>(node)->circuitTestBegin(tptr,test,reportCallback);
+ return reinterpret_cast<ZeroTier::Node *>(node)->setPhysicalPathConfiguration(pathNetwork,pathConfig);
} catch ( ... ) {
return ZT_RESULT_FATAL_ERROR_INTERNAL;
}
}
-void ZT_Node_circuitTestEnd(ZT_Node *node,ZT_CircuitTest *test)
-{
- try {
- reinterpret_cast<ZeroTier::Node *>(node)->circuitTestEnd(test);
- } catch ( ... ) {}
-}
-
-enum ZT_ResultCode ZT_Node_clusterInit(
- ZT_Node *node,
- unsigned int myId,
- const struct sockaddr_storage *zeroTierPhysicalEndpoints,
- unsigned int numZeroTierPhysicalEndpoints,
- int x,
- int y,
- int z,
- void (*sendFunction)(void *,unsigned int,const void *,unsigned int),
- void *sendFunctionArg,
- int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *),
- void *addressToLocationFunctionArg)
-{
- try {
- return reinterpret_cast<ZeroTier::Node *>(node)->clusterInit(myId,zeroTierPhysicalEndpoints,numZeroTierPhysicalEndpoints,x,y,z,sendFunction,sendFunctionArg,addressToLocationFunction,addressToLocationFunctionArg);
- } catch ( ... ) {
- return ZT_RESULT_FATAL_ERROR_INTERNAL;
- }
-}
-
-enum ZT_ResultCode ZT_Node_clusterAddMember(ZT_Node *node,unsigned int memberId)
-{
- try {
- return reinterpret_cast<ZeroTier::Node *>(node)->clusterAddMember(memberId);
- } catch ( ... ) {
- return ZT_RESULT_FATAL_ERROR_INTERNAL;
- }
-}
-
-void ZT_Node_clusterRemoveMember(ZT_Node *node,unsigned int memberId)
-{
- try {
- reinterpret_cast<ZeroTier::Node *>(node)->clusterRemoveMember(memberId);
- } catch ( ... ) {}
-}
-
-void ZT_Node_clusterHandleIncomingMessage(ZT_Node *node,const void *msg,unsigned int len)
-{
- try {
- reinterpret_cast<ZeroTier::Node *>(node)->clusterHandleIncomingMessage(msg,len);
- } catch ( ... ) {}
-}
-
-void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs)
-{
- try {
- reinterpret_cast<ZeroTier::Node *>(node)->clusterStatus(cs);
- } catch ( ... ) {}
-}
-
-void ZT_Node_setTrustedPaths(ZT_Node *node,const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count)
-{
- try {
- reinterpret_cast<ZeroTier::Node *>(node)->setTrustedPaths(networks,ids,count);
- } catch ( ... ) {}
-}
-
void ZT_version(int *major,int *minor,int *revision)
{
if (major) *major = ZEROTIER_ONE_VERSION_MAJOR;
diff --git a/node/Node.hpp b/node/Node.hpp
index d25a619b..79284b63 100644
--- a/node/Node.hpp
+++ b/node/Node.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_NODE_HPP
@@ -38,13 +46,7 @@
#include "Path.hpp"
#include "Salsa20.hpp"
#include "NetworkController.hpp"
-
-#undef TRACE
-#ifdef ZT_TRACE
-#define TRACE(f,...) RR->node->postTrace(__FILE__,__LINE__,f,##__VA_ARGS__)
-#else
-#define TRACE(f,...) {}
-#endif
+#include "Hashtable.hpp"
// Bit mask for "expecting reply" hash
#define ZT_EXPECTING_REPLIES_BUCKET_MASK1 255
@@ -62,7 +64,7 @@ class World;
class Node : public NetworkController::Sender
{
public:
- Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now);
+ Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64_t now);
virtual ~Node();
// Get rid of alignment warnings on 32-bit Windows and possibly improve performance
@@ -75,15 +77,15 @@ public:
ZT_ResultCode processWirePacket(
void *tptr,
- uint64_t now,
- const struct sockaddr_storage *localAddress,
+ int64_t now,
+ int64_t localSocket,
const struct sockaddr_storage *remoteAddress,
const void *packetData,
unsigned int packetLength,
- volatile uint64_t *nextBackgroundTaskDeadline);
+ volatile int64_t *nextBackgroundTaskDeadline);
ZT_ResultCode processVirtualNetworkFrame(
void *tptr,
- uint64_t now,
+ int64_t now,
uint64_t nwid,
uint64_t sourceMac,
uint64_t destMac,
@@ -91,8 +93,8 @@ public:
unsigned int vlanId,
const void *frameData,
unsigned int frameLength,
- volatile uint64_t *nextBackgroundTaskDeadline);
- ZT_ResultCode processBackgroundTasks(void *tptr,uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline);
+ volatile int64_t *nextBackgroundTaskDeadline);
+ ZT_ResultCode processBackgroundTasks(void *tptr,int64_t now,volatile int64_t *nextBackgroundTaskDeadline);
ZT_ResultCode join(uint64_t nwid,void *uptr,void *tptr);
ZT_ResultCode leave(uint64_t nwid,void **uptr,void *tptr);
ZT_ResultCode multicastSubscribe(void *tptr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
@@ -109,35 +111,18 @@ public:
void clearLocalInterfaceAddresses();
int sendUserMessage(void *tptr,uint64_t dest,uint64_t typeId,const void *data,unsigned int len);
void setNetconfMaster(void *networkControllerInstance);
- ZT_ResultCode circuitTestBegin(void *tptr,ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *));
- void circuitTestEnd(ZT_CircuitTest *test);
- ZT_ResultCode clusterInit(
- unsigned int myId,
- const struct sockaddr_storage *zeroTierPhysicalEndpoints,
- unsigned int numZeroTierPhysicalEndpoints,
- int x,
- int y,
- int z,
- void (*sendFunction)(void *,unsigned int,const void *,unsigned int),
- void *sendFunctionArg,
- int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *),
- void *addressToLocationFunctionArg);
- ZT_ResultCode clusterAddMember(unsigned int memberId);
- void clusterRemoveMember(unsigned int memberId);
- void clusterHandleIncomingMessage(const void *msg,unsigned int len);
- void clusterStatus(ZT_ClusterStatus *cs);
// Internal functions ------------------------------------------------------
- inline uint64_t now() const throw() { return _now; }
+ inline int64_t now() const { return _now; }
- inline bool putPacket(void *tPtr,const InetAddress &localAddress,const InetAddress &addr,const void *data,unsigned int len,unsigned int ttl = 0)
+ inline bool putPacket(void *tPtr,const int64_t localSocket,const InetAddress &addr,const void *data,unsigned int len,unsigned int ttl = 0)
{
return (_cb.wirePacketSendFunction(
reinterpret_cast<ZT_Node *>(this),
_uPtr,
tPtr,
- reinterpret_cast<const struct sockaddr_storage *>(&localAddress),
+ localSocket,
reinterpret_cast<const struct sockaddr_storage *>(&addr),
data,
len,
@@ -163,26 +148,27 @@ public:
inline SharedPtr<Network> network(uint64_t nwid) const
{
Mutex::Lock _l(_networks_m);
- return _network(nwid);
+ const SharedPtr<Network> *n = _networks.get(nwid);
+ if (n)
+ return *n;
+ return SharedPtr<Network>();
}
inline bool belongsToNetwork(uint64_t nwid) const
{
Mutex::Lock _l(_networks_m);
- for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i) {
- if (i->first == nwid)
- return true;
- }
- return false;
+ return _networks.contains(nwid);
}
inline std::vector< SharedPtr<Network> > allNetworks() const
{
std::vector< SharedPtr<Network> > nw;
Mutex::Lock _l(_networks_m);
- nw.reserve(_networks.size());
- for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i)
- nw.push_back(i->second);
+ Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(*const_cast< Hashtable< uint64_t,SharedPtr<Network> > * >(&_networks));
+ uint64_t *k = (uint64_t *)0;
+ SharedPtr<Network> *v = (SharedPtr<Network> *)0;
+ while (i.next(k,v))
+ nw.push_back(*v);
return nw;
}
@@ -192,31 +178,27 @@ public:
return _directPaths;
}
- inline bool dataStorePut(void *tPtr,const char *name,const void *data,unsigned int len,bool secure) { return (_cb.dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,name,data,len,(int)secure) == 0); }
- inline bool dataStorePut(void *tPtr,const char *name,const std::string &data,bool secure) { return dataStorePut(tPtr,name,(const void *)data.data(),(unsigned int)data.length(),secure); }
- inline void dataStoreDelete(void *tPtr,const char *name) { _cb.dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,name,(const void *)0,0,0); }
- std::string dataStoreGet(void *tPtr,const char *name);
-
inline void postEvent(void *tPtr,ZT_Event ev,const void *md = (const void *)0) { _cb.eventCallback(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,ev,md); }
inline int configureVirtualNetworkPort(void *tPtr,uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { return _cb.virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,nwid,nuptr,op,nc); }
- inline bool online() const throw() { return _online; }
+ inline bool online() const { return _online; }
-#ifdef ZT_TRACE
- void postTrace(const char *module,unsigned int line,const char *fmt,...);
-#endif
+ inline int stateObjectGet(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2],void *const data,const unsigned int maxlen) { return _cb.stateGetFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,type,id,data,maxlen); }
+ inline void stateObjectPut(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2],const void *const data,const unsigned int len) { _cb.statePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,type,id,data,(int)len); }
+ inline void stateObjectDelete(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2]) { _cb.statePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,type,id,(const void *)0,-1); }
- bool shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,const InetAddress &localAddress,const InetAddress &remoteAddress);
+ bool shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,const int64_t localSocket,const InetAddress &remoteAddress);
inline bool externalPathLookup(void *tPtr,const Address &ztaddr,int family,InetAddress &addr) { return ( (_cb.pathLookupFunction) ? (_cb.pathLookupFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,ztaddr.toInt(),family,reinterpret_cast<struct sockaddr_storage *>(&addr)) != 0) : false ); }
uint64_t prng();
- void postCircuitTestReport(const ZT_CircuitTestReport *report);
- void setTrustedPaths(const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count);
+ ZT_ResultCode setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig);
World planet() const;
std::vector<World> moons() const;
+ inline const Identity &identity() const { return _RR.identity; }
+
/**
* Register that we are expecting a reply to a packet ID
*
@@ -261,7 +243,7 @@ public:
* @param from Source address of packet
* @return True if within rate limits
*/
- inline bool rateGateIdentityVerification(const uint64_t now,const InetAddress &from)
+ inline bool rateGateIdentityVerification(const int64_t now,const InetAddress &from)
{
unsigned long iph = from.rateGateHash();
if ((now - _lastIdentityVerification[iph]) >= ZT_IDENTITY_VALIDATION_SOURCE_RATE_LIMIT) {
@@ -275,17 +257,10 @@ public:
virtual void ncSendRevocation(const Address &destination,const Revocation &rev);
virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode);
-private:
- inline SharedPtr<Network> _network(uint64_t nwid) const
- {
- // assumes _networks_m is locked
- for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i) {
- if (i->first == nwid)
- return i->second;
- }
- return SharedPtr<Network>();
- }
+ inline const Address &remoteTraceTarget() const { return _remoteTraceTarget; }
+ inline Trace::Level remoteTraceLevel() const { return _remoteTraceLevel; }
+private:
RuntimeEnvironment _RR;
RuntimeEnvironment *RR;
void *_uPtr; // _uptr (lower case) is reserved in Visual Studio :P
@@ -296,23 +271,24 @@ private:
uint32_t _expectingRepliesTo[ZT_EXPECTING_REPLIES_BUCKET_MASK1 + 1][ZT_EXPECTING_REPLIES_BUCKET_MASK2 + 1];
// Time of last identity verification indexed by InetAddress.rateGateHash() -- used in IncomingPacket::_doHELLO() via rateGateIdentityVerification()
- uint64_t _lastIdentityVerification[16384];
+ int64_t _lastIdentityVerification[16384];
- std::vector< std::pair< uint64_t, SharedPtr<Network> > > _networks;
+ Hashtable< uint64_t,SharedPtr<Network> > _networks;
Mutex _networks_m;
- std::vector< ZT_CircuitTest * > _circuitTests;
- Mutex _circuitTests_m;
-
std::vector<InetAddress> _directPaths;
Mutex _directPaths_m;
Mutex _backgroundTasksLock;
- uint64_t _now;
- uint64_t _lastPingCheck;
- uint64_t _lastHousekeepingRun;
- volatile uint64_t _prngState[2];
+ Address _remoteTraceTarget;
+ enum Trace::Level _remoteTraceLevel;
+
+ volatile int64_t _now;
+ int64_t _lastPingCheck;
+ int64_t _lastHousekeepingRun;
+ int64_t _lastMemoizedTraceSettings;
+ volatile int64_t _prngState[2];
bool _online;
};
diff --git a/node/OutboundMulticast.cpp b/node/OutboundMulticast.cpp
index 285bfa5d..d7a7b4d8 100644
--- a/node/OutboundMulticast.cpp
+++ b/node/OutboundMulticast.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include "Constants.hpp"
@@ -57,18 +65,6 @@ void OutboundMulticast::init(
if (gatherLimit) flags |= 0x02;
- /*
- TRACE(">>MC %.16llx INIT %.16llx/%s limit %u gatherLimit %u from %s to %s length %u",
- (unsigned long long)this,
- nwid,
- dest.toString().c_str(),
- limit,
- gatherLimit,
- (src) ? src.toString().c_str() : MAC(RR->identity.address(),nwid).toString().c_str(),
- dest.toString().c_str(),
- len);
- */
-
_packet.setSource(RR->identity.address());
_packet.setVerb(Packet::VERB_MULTICAST_FRAME);
_packet.append((uint64_t)nwid);
@@ -82,7 +78,7 @@ void OutboundMulticast::init(
if (!disableCompression)
_packet.compress();
- memcpy(_frameData,payload,_frameLen);
+ ZT_FAST_MEMCPY(_frameData,payload,_frameLen);
}
void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,void *tPtr,const Address &toAddr)
@@ -90,7 +86,6 @@ void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,void *tPtr,const A
const SharedPtr<Network> nw(RR->node->network(_nwid));
const Address toAddr2(toAddr);
if ((nw)&&(nw->filterOutgoingPacket(tPtr,true,RR->identity.address(),toAddr2,_macSrc,_macDest,_frameData,_frameLen,_etherType,0))) {
- //TRACE(">>MC %.16llx -> %s",(unsigned long long)this,toAddr.toString().c_str());
_packet.newInitializationVector();
_packet.setDestination(toAddr2);
RR->node->expectReplyTo(_packet.packetId());
diff --git a/node/OutboundMulticast.hpp b/node/OutboundMulticast.hpp
index 0ecf113f..a735f52b 100644
--- a/node/OutboundMulticast.hpp
+++ b/node/OutboundMulticast.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_OUTBOUNDMULTICAST_HPP
@@ -82,18 +90,18 @@ public:
/**
* @return Multicast creation time
*/
- inline uint64_t timestamp() const throw() { return _timestamp; }
+ inline uint64_t timestamp() const { return _timestamp; }
/**
* @param now Current time
* @return True if this multicast is expired (has exceeded transmit timeout)
*/
- inline bool expired(uint64_t now) const throw() { return ((now - _timestamp) >= ZT_MULTICAST_TRANSMIT_TIMEOUT); }
+ inline bool expired(int64_t now) const { return ((now - _timestamp) >= ZT_MULTICAST_TRANSMIT_TIMEOUT); }
/**
* @return True if this outbound multicast has been sent to enough peers
*/
- inline bool atLimit() const throw() { return (_alreadySentTo.size() >= _limit); }
+ inline bool atLimit() const { return (_alreadySentTo.size() >= _limit); }
/**
* Just send without checking log
@@ -118,6 +126,16 @@ public:
}
/**
+ * Log an address as having been used so we will not send there in the future
+ *
+ * @param toAddr Address to log as sent
+ */
+ inline void logAsSent(const Address &toAddr)
+ {
+ _alreadySentTo.push_back(toAddr);
+ }
+
+ /**
* Try to send this to a given peer if it hasn't been sent to them already
*
* @param RR Runtime environment
diff --git a/node/Packet.cpp b/node/Packet.cpp
index 8a57dd55..2eeceffa 100644
--- a/node/Packet.cpp
+++ b/node/Packet.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdint.h>
@@ -147,53 +155,22 @@ namespace {
A library is provided to take care of it, see lz4frame.h.
*/
-#define LZ4LIB_API
-
-/*========== Version =========== */
#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */
#define LZ4_VERSION_MINOR 7 /* for new (non-breaking) interface capabilities */
#define LZ4_VERSION_RELEASE 5 /* for tweaks, bug-fixes, or development */
-
#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
-
#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE
#define LZ4_QUOTE(str) #str
#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str)
#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION)
-
-/*!
- * LZ4_MEMORY_USAGE :
- * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
- * Increasing memory usage improves compression ratio
- * Reduced memory usage can improve speed, due to cache effect
- * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache
- */
#define LZ4_MEMORY_USAGE 14
-
#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */
#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
-/*-*********************************************
-* Streaming Compression Functions
-***********************************************/
typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */
-/*! LZ4_resetStream() :
- * An LZ4_stream_t structure can be allocated once and re-used multiple times.
- * Use this function to init an allocated `LZ4_stream_t` structure and start a new compression.
- */
-LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr);
-
-/*^**********************************************
- * !!!!!! STATIC LINKING ONLY !!!!!!
- ***********************************************/
-/*-************************************
- * Private definitions
- **************************************
- * Do not use these definitions.
- * They are exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`.
- * Using these definitions will expose code to API and/or ABI break in future versions of the library.
- **************************************/
+static inline void LZ4_resetStream (LZ4_stream_t* streamPtr);
+
#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2)
#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)
#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */
@@ -214,14 +191,6 @@ typedef struct {
size_t prefixSize;
} LZ4_streamDecode_t_internal;
-/*!
- * LZ4_stream_t :
- * information structure to track an LZ4 stream.
- * init this structure before first use.
- * note : only use in association with static linking !
- * this definition is not API/ABI safe,
- * and may change in a future version !
- */
#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4)
#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(unsigned long long))
union LZ4_stream_u {
@@ -229,14 +198,6 @@ union LZ4_stream_u {
LZ4_stream_t_internal internal_donotuse;
} ; /* previously typedef'd to LZ4_stream_t */
-/*!
- * LZ4_streamDecode_t :
- * information structure to track an LZ4 stream during decompression.
- * init this structure using LZ4_setStreamDecode (or memset()) before first use
- * note : only use in association with static linking !
- * this definition is not API/ABI safe,
- * and may change in a future version !
- */
#define LZ4_STREAMDECODESIZE_U64 4
#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long))
union LZ4_streamDecode_u {
@@ -245,33 +206,7 @@ union LZ4_streamDecode_u {
} ; /* previously typedef'd to LZ4_streamDecode_t */
#ifndef HEAPMODE
-# define HEAPMODE 0
-#endif
-
-//#define ACCELERATION_DEFAULT 1
-
-/* LZ4_FORCE_MEMORY_ACCESS
- * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.
- * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.
- * The below switch allow to select different access method for improved performance.
- * Method 0 (default) : use `memcpy()`. Safe and portable.
- * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).
- * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.
- * Method 2 : direct access. This method is portable but violate C standard.
- * It can generate buggy code on targets which generate assembly depending on alignment.
- * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)
- * See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details.
- * Prefer these methods in priority order (0 > 1 > 2)
- */
-#if 0
-#ifndef LZ4_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */
-# if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )
-# define LZ4_FORCE_MEMORY_ACCESS 2
-# elif defined(__INTEL_COMPILER) || \
- (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) ))
-# define LZ4_FORCE_MEMORY_ACCESS 1
-# endif
-#endif
+#define HEAPMODE 0
#endif
#ifdef ZT_NO_TYPE_PUNNING
@@ -280,59 +215,18 @@ union LZ4_streamDecode_u {
#define LZ4_FORCE_MEMORY_ACCESS 2
#endif
-/*
- * LZ4_FORCE_SW_BITCOUNT
- * Define this parameter if your target system or compiler does not support hardware bit count
- */
#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for Windows CE does not support Hardware bit count */
-# define LZ4_FORCE_SW_BITCOUNT
-#endif
-
-/*-************************************
-* Compiler Options
-**************************************/
-#if 0
-#ifdef _MSC_VER /* Visual Studio */
-# define FORCE_INLINE static __forceinline
-# include <intrin.h>
-# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
-# pragma warning(disable : 4293) /* disable: C4293: too large shift (32-bits) */
-#else
-# if defined(__GNUC__) || defined(__clang__)
-# define FORCE_INLINE static inline __attribute__((always_inline))
-# elif defined(__cplusplus) || (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
-# define FORCE_INLINE static inline
-# else
-# define FORCE_INLINE static
-# endif
-#endif /* _MSC_VER */
+#define LZ4_FORCE_SW_BITCOUNT
#endif
#ifndef FORCE_INLINE
#define FORCE_INLINE static inline
#endif
-#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__)
-# define expect(expr,value) (__builtin_expect ((expr),(value)) )
-#else
-# define expect(expr,value) (expr)
-#endif
-
-#define likely(expr) expect((expr) != 0, 1)
-#define unlikely(expr) expect((expr) != 0, 0)
-
-/*-************************************
-* Memory routines
-**************************************/
-//#include <stdlib.h> /* malloc, calloc, free */
#define ALLOCATOR(n,s) calloc(n,s)
#define FREEMEM free
-//#include <string.h> /* memset, memcpy */
#define MEM_INIT memset
-/*-************************************
-* Basic Types
-**************************************/
typedef uint8_t BYTE;
typedef uint16_t U16;
typedef uint32_t U32;
@@ -341,65 +235,46 @@ typedef uint64_t U64;
typedef uintptr_t uptrval;
typedef uintptr_t reg_t;
-/*-************************************
-* Reading and writing into memory
-**************************************/
-static unsigned LZ4_isLittleEndian(void)
+static inline unsigned LZ4_isLittleEndian(void)
{
const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */
return one.c[0];
}
#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2)
-/* lie to the compiler about data alignment; use with caution */
-
static U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; }
static U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; }
static reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; }
-
static void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; }
static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; }
-
#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1)
-
-/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */
-/* currently only defined for gcc and icc */
typedef union { U16 u16; U32 u32; reg_t uArch; } __attribute__((packed)) unalign;
-
static U16 LZ4_read16(const void* ptr) { return ((const unalign*)ptr)->u16; }
static U32 LZ4_read32(const void* ptr) { return ((const unalign*)ptr)->u32; }
static reg_t LZ4_read_ARCH(const void* ptr) { return ((const unalign*)ptr)->uArch; }
-
static void LZ4_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; }
static void LZ4_write32(void* memPtr, U32 value) { ((unalign*)memPtr)->u32 = value; }
-
#else /* safe and portable access through memcpy() */
-
static inline U16 LZ4_read16(const void* memPtr)
{
- U16 val; memcpy(&val, memPtr, sizeof(val)); return val;
+ U16 val; ZT_FAST_MEMCPY(&val, memPtr, sizeof(val)); return val;
}
-
static inline U32 LZ4_read32(const void* memPtr)
{
- U32 val; memcpy(&val, memPtr, sizeof(val)); return val;
+ U32 val; ZT_FAST_MEMCPY(&val, memPtr, sizeof(val)); return val;
}
-
static inline reg_t LZ4_read_ARCH(const void* memPtr)
{
- reg_t val; memcpy(&val, memPtr, sizeof(val)); return val;
+ reg_t val; ZT_FAST_MEMCPY(&val, memPtr, sizeof(val)); return val;
}
-
static inline void LZ4_write16(void* memPtr, U16 value)
{
- memcpy(memPtr, &value, sizeof(value));
+ ZT_FAST_MEMCPY(memPtr, &value, sizeof(value));
}
-
static inline void LZ4_write32(void* memPtr, U32 value)
{
- memcpy(memPtr, &value, sizeof(value));
+ ZT_FAST_MEMCPY(memPtr, &value, sizeof(value));
}
-
#endif /* LZ4_FORCE_MEMORY_ACCESS */
static inline U16 LZ4_readLE16(const void* memPtr)
@@ -425,22 +300,17 @@ static inline void LZ4_writeLE16(void* memPtr, U16 value)
static inline void LZ4_copy8(void* dst, const void* src)
{
- memcpy(dst,src,8);
+ ZT_FAST_MEMCPY(dst,src,8);
}
-/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */
static inline void LZ4_wildCopy(void* dstPtr, const void* srcPtr, void* dstEnd)
{
BYTE* d = (BYTE*)dstPtr;
const BYTE* s = (const BYTE*)srcPtr;
BYTE* const e = (BYTE*)dstEnd;
-
do { LZ4_copy8(d,s); d+=8; s+=8; } while (d<e);
}
-/*-************************************
-* Common Constants
-**************************************/
#define MINMATCH 4
#define WILDCOPYLENGTH 8
@@ -460,14 +330,8 @@ static const int LZ4_minLength = (MFLIMIT+1);
#define RUN_BITS (8-ML_BITS)
#define RUN_MASK ((1U<<RUN_BITS)-1)
-/*-************************************
-* Common Utils
-**************************************/
#define LZ4_STATIC_ASSERT(c) { enum { LZ4_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */
-/*-************************************
-* Common functions
-**************************************/
static inline unsigned LZ4_NbCommonBytes (register reg_t val)
{
if (LZ4_isLittleEndian()) {
@@ -544,15 +408,9 @@ static inline unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE
return (unsigned)(pIn - pStart);
}
-/*-************************************
-* Local Constants
-**************************************/
static const int LZ4_64Klimit = ((64 KB) + (MFLIMIT-1));
static const U32 LZ4_skipTrigger = 6; /* Increase this value ==> compression run slower on incompressible data */
-/*-************************************
-* Local Structures and types
-**************************************/
typedef enum { notLimited = 0, limitedOutput = 1 } limitedOutput_directive;
typedef enum { byPtr, byU32, byU16 } tableType_t;
@@ -562,17 +420,8 @@ typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive;
typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive;
typedef enum { full = 0, partial = 1 } earlyEnd_directive;
-/*-************************************
-* Local Utils
-**************************************/
-//int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; }
-//const char* LZ4_versionString(void) { return LZ4_VERSION_STRING; }
-int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); }
-//int LZ4_sizeofState() { return LZ4_STREAMSIZE; }
-
-/*-******************************
-* Compression functions
-********************************/
+static inline int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); }
+
static inline U32 LZ4_hash4(U32 sequence, tableType_t const tableType)
{
if (tableType == byU16)
@@ -627,8 +476,6 @@ FORCE_INLINE const BYTE* LZ4_getPosition(const BYTE* p, void* tableBase, tableTy
return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase);
}
-/** LZ4_compress_generic() :
- inlined, to ensure branches are decided at compilation time */
FORCE_INLINE int LZ4_compress_generic(
LZ4_stream_t_internal* const cctx,
const char* const source,
@@ -820,7 +667,7 @@ _last_literals:
} else {
*op++ = (BYTE)(lastRun<<ML_BITS);
}
- memcpy(op, anchor, lastRun);
+ ZT_FAST_MEMCPY(op, anchor, lastRun);
op += lastRun;
}
@@ -864,20 +711,11 @@ static inline int LZ4_compress_fast(const char* source, char* dest, int inputSiz
return result;
}
-void LZ4_resetStream (LZ4_stream_t* LZ4_stream)
+static inline void LZ4_resetStream (LZ4_stream_t* LZ4_stream)
{
MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t));
}
-/*-*****************************
-* Decompression functions
-*******************************/
-/*! LZ4_decompress_generic() :
- * This generic decompression function cover all use cases.
- * It shall be instantiated several times, using different sets of directives
- * Note that it is important this generic function is really inlined,
- * in order to remove useless branches during compilation optimization.
- */
FORCE_INLINE int LZ4_decompress_generic(
const char* const source,
char* const dest,
@@ -946,7 +784,7 @@ FORCE_INLINE int LZ4_decompress_generic(
if ((!endOnInput) && (cpy != oend)) goto _output_error; /* Error : block decoding must stop exactly there */
if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) goto _output_error; /* Error : input must be consumed */
}
- memcpy(op, ip, length);
+ ZT_FAST_MEMCPY(op, ip, length);
ip += length;
op += length;
break; /* Necessarily EOF, due to parsing restrictions */
@@ -985,14 +823,14 @@ FORCE_INLINE int LZ4_decompress_generic(
/* match encompass external dictionary and current block */
size_t const copySize = (size_t)(lowPrefix-match);
size_t const restSize = length - copySize;
- memcpy(op, dictEnd - copySize, copySize);
+ ZT_FAST_MEMCPY(op, dictEnd - copySize, copySize);
op += copySize;
if (restSize > (size_t)(op-lowPrefix)) { /* overlap copy */
BYTE* const endOfMatch = op + restSize;
const BYTE* copyFrom = lowPrefix;
while (op < endOfMatch) *op++ = *copyFrom++;
} else {
- memcpy(op, lowPrefix, restSize);
+ ZT_FAST_MEMCPY(op, lowPrefix, restSize);
op += restSize;
} }
continue;
@@ -1007,7 +845,7 @@ FORCE_INLINE int LZ4_decompress_generic(
op[2] = match[2];
op[3] = match[3];
match += dec32table[offset];
- memcpy(op+4, match, 4);
+ ZT_FAST_MEMCPY(op+4, match, 4);
match -= dec64;
} else { LZ4_copy8(op, match); match+=8; }
op += 8;
@@ -1051,64 +889,16 @@ static inline int LZ4_decompress_safe(const char* source, char* dest, int compre
const unsigned char Packet::ZERO_KEY[32] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
-#ifdef ZT_TRACE
-
-const char *Packet::verbString(Verb v)
-{
- switch(v) {
- case VERB_NOP: return "NOP";
- case VERB_HELLO: return "HELLO";
- case VERB_ERROR: return "ERROR";
- case VERB_OK: return "OK";
- case VERB_WHOIS: return "WHOIS";
- case VERB_RENDEZVOUS: return "RENDEZVOUS";
- case VERB_FRAME: return "FRAME";
- case VERB_EXT_FRAME: return "EXT_FRAME";
- case VERB_ECHO: return "ECHO";
- case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE";
- case VERB_NETWORK_CREDENTIALS: return "NETWORK_CREDENTIALS";
- case VERB_NETWORK_CONFIG_REQUEST: return "NETWORK_CONFIG_REQUEST";
- case VERB_NETWORK_CONFIG: return "NETWORK_CONFIG";
- case VERB_MULTICAST_GATHER: return "MULTICAST_GATHER";
- case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME";
- case VERB_PUSH_DIRECT_PATHS: return "PUSH_DIRECT_PATHS";
- case VERB_CIRCUIT_TEST: return "CIRCUIT_TEST";
- case VERB_CIRCUIT_TEST_REPORT: return "CIRCUIT_TEST_REPORT";
- case VERB_USER_MESSAGE: return "USER_MESSAGE";
- }
- return "(unknown)";
-}
-
-const char *Packet::errorString(ErrorCode e)
-{
- switch(e) {
- case ERROR_NONE: return "NONE";
- case ERROR_INVALID_REQUEST: return "INVALID_REQUEST";
- case ERROR_BAD_PROTOCOL_VERSION: return "BAD_PROTOCOL_VERSION";
- case ERROR_OBJ_NOT_FOUND: return "OBJECT_NOT_FOUND";
- case ERROR_IDENTITY_COLLISION: return "IDENTITY_COLLISION";
- case ERROR_UNSUPPORTED_OPERATION: return "UNSUPPORTED_OPERATION";
- case ERROR_NEED_MEMBERSHIP_CERTIFICATE: return "NEED_MEMBERSHIP_CERTIFICATE";
- case ERROR_NETWORK_ACCESS_DENIED_: return "NETWORK_ACCESS_DENIED";
- case ERROR_UNWANTED_MULTICAST: return "UNWANTED_MULTICAST";
- }
- return "(unknown)";
-}
-
-#endif // ZT_TRACE
-
-void Packet::armor(const void *key,bool encryptPayload,unsigned int counter)
+void Packet::armor(const void *key,bool encryptPayload)
{
uint8_t mangledKey[32];
uint8_t *const data = reinterpret_cast<uint8_t *>(unsafeData());
- // Mask least significant 3 bits of packet ID with counter to embed packet send counter for QoS use
- data[7] = (data[7] & 0xf8) | (uint8_t)(counter & 0x07);
-
// Set flag now, since it affects key mangle function
setCipher(encryptPayload ? ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012 : ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE);
_salsa20MangleKey((const unsigned char *)key,mangledKey);
+
if (ZT_HAS_FAST_CRYPTO()) {
const unsigned int encryptLen = (encryptPayload) ? (size() - ZT_PACKET_IDX_VERB) : 0;
uint64_t keyStream[(ZT_PROTO_MAX_PACKET_LENGTH + 64 + 8) / 8];
@@ -1131,7 +921,7 @@ void Packet::armor(const void *key,bool encryptPayload,unsigned int counter)
s20.crypt12(payload,payload,payloadLen);
uint64_t mac[2];
Poly1305::compute(mac,payload,payloadLen,macKey);
- memcpy(data + ZT_PACKET_IDX_MAC,mac,8);
+ ZT_FAST_MEMCPY(data + ZT_PACKET_IDX_MAC,mac,8);
}
}
@@ -1203,7 +993,7 @@ bool Packet::compress()
if ((cl > 0)&&(cl < pl)) {
data[ZT_PACKET_IDX_VERB] |= (char)ZT_PROTO_VERB_FLAG_COMPRESSED;
setSize((unsigned int)cl + ZT_PACKET_IDX_PAYLOAD);
- memcpy(data + ZT_PACKET_IDX_PAYLOAD,buf,cl);
+ ZT_FAST_MEMCPY(data + ZT_PACKET_IDX_PAYLOAD,buf,cl);
return true;
}
}
@@ -1223,7 +1013,7 @@ bool Packet::uncompress()
int ucl = LZ4_decompress_safe((const char *)data + ZT_PACKET_IDX_PAYLOAD,buf,compLen,sizeof(buf));
if ((ucl > 0)&&(ucl <= (int)(capacity() - ZT_PACKET_IDX_PAYLOAD))) {
setSize((unsigned int)ucl + ZT_PACKET_IDX_PAYLOAD);
- memcpy(data + ZT_PACKET_IDX_PAYLOAD,buf,ucl);
+ ZT_FAST_MEMCPY(data + ZT_PACKET_IDX_PAYLOAD,buf,ucl);
} else {
return false;
}
diff --git a/node/Packet.hpp b/node/Packet.hpp
index 8ad2c0f9..27da6fb5 100644
--- a/node/Packet.hpp
+++ b/node/Packet.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_N_PACKET_HPP
@@ -34,12 +42,6 @@
#include "Utils.hpp"
#include "Buffer.hpp"
-//#ifdef ZT_USE_SYSTEM_LZ4
-//#include <lz4.h>
-//#else
-//#include "../ext/lz4/lz4.h"
-//#endif
-
/**
* Protocol version -- incremented only for major changes
*
@@ -53,7 +55,7 @@
* 4 - 0.6.0 ... 1.0.6
* + BREAKING CHANGE: New identity format based on hashcash design
* 5 - 1.1.0 ... 1.1.5
- * + Supports circuit test, proof of work, and echo
+ * + Supports echo
* + Supports in-band world (root server definition) updates
* + Clustering! (Though this will work with protocol v4 clients.)
* + Otherwise backward compatible with protocol v4
@@ -65,9 +67,7 @@
* + Multipart network configurations for large network configs
* + Tags and Capabilities
* + Inline push of CertificateOfMembership deprecated
- * + Certificates of representation for federation and mesh
* 9 - 1.2.0 ... CURRENT
- * + In-band encoding of packet counter for link quality measurement
*/
#define ZT_PROTO_VERSION 9
@@ -223,12 +223,8 @@
/**
* Packet buffer size (can be changed)
- *
- * The current value is big enough for ZT_MAX_PACKET_FRAGMENTS, the pragmatic
- * packet fragment limit, times the default UDP MTU. Most packets won't be
- * this big.
*/
-#define ZT_PROTO_MAX_PACKET_LENGTH (ZT_MAX_PACKET_FRAGMENTS * ZT_UDP_DEFAULT_PAYLOAD_MTU)
+#define ZT_PROTO_MAX_PACKET_LENGTH (ZT_MAX_PACKET_FRAGMENTS * ZT_DEFAULT_PHYSMTU)
/**
* Minimum viable packet length (a.k.a. header length)
@@ -422,8 +418,7 @@ public:
}
template<unsigned int C2>
- Fragment(const Buffer<C2> &b)
- throw(std::out_of_range) :
+ Fragment(const Buffer<C2> &b) :
Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(b)
{
}
@@ -441,10 +436,8 @@ public:
* @param fragLen Length of fragment in bytes
* @param fragNo Which fragment (>= 1, since 0 is Packet with end chopped off)
* @param fragTotal Total number of fragments (including 0)
- * @throws std::out_of_range Packet size would exceed buffer
*/
Fragment(const Packet &p,unsigned int fragStart,unsigned int fragLen,unsigned int fragNo,unsigned int fragTotal)
- throw(std::out_of_range)
{
init(p,fragStart,fragLen,fragNo,fragTotal);
}
@@ -457,13 +450,11 @@ public:
* @param fragLen Length of fragment in bytes
* @param fragNo Which fragment (>= 1, since 0 is Packet with end chopped off)
* @param fragTotal Total number of fragments (including 0)
- * @throws std::out_of_range Packet size would exceed buffer
*/
inline void init(const Packet &p,unsigned int fragStart,unsigned int fragLen,unsigned int fragNo,unsigned int fragTotal)
- throw(std::out_of_range)
{
if ((fragStart + fragLen) > p.size())
- throw std::out_of_range("Packet::Fragment: tried to construct fragment of packet past its length");
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
setSize(fragLen + ZT_PROTO_MIN_FRAGMENT_LENGTH);
// NOTE: this copies both the IV/packet ID and the destination address.
@@ -557,8 +548,6 @@ public:
* [<[8] 64-bit world ID of moon>]
* [<[8] 64-bit timestamp of moon>]
* [... additional moon type/ID/timestamp tuples ...]
- * <[2] 16-bit length of certificate of representation>
- * [... certificate of representation ...]
*
* HELLO is sent in the clear as it is how peers share their identity
* public keys. A few additional fields are sent in the clear too, but
@@ -580,8 +569,6 @@ public:
* <[...] physical destination address of packet>
* <[2] 16-bit length of world update(s) or 0 if none>
* [[...] updates to planets and/or moons]
- * <[2] 16-bit length of certificate of representation>
- * [... certificate of representation ...]
*
* With the exception of the timestamp, the other fields pertain to the
* respondent who is sending OK and are not echoes.
@@ -687,7 +674,7 @@ public:
* 0x5 - REDIRECTed inbound frame
* 0x6 - WATCHed inbound frame
* 0x7 - (reserved for future use)
- *
+ *
* An extended frame carries full MAC addressing, making it a
* superset of VERB_FRAME. It is used for bridged traffic,
* redirected or observed traffic via rules, and can in theory
@@ -769,7 +756,7 @@ public:
*
* It would be valid and correct as of 1.2.0 to use NETWORK_CONFIG always,
* but OK(NTEWORK_CONFIG_REQUEST) should be sent for compatibility.
- *
+ *
* OK response payload:
* <[8] 64-bit network ID>
* <[2] 16-bit length of network configuration dictionary chunk>
@@ -884,6 +871,7 @@ public:
* 0x01 - Network certificate of membership attached (DEPRECATED)
* 0x02 - Implicit gather limit field is present
* 0x04 - Source MAC is specified -- otherwise it's computed from sender
+ * 0x08 - Please replicate (sent to multicast replicators)
*
* OK and ERROR responses are optional. OK may be generated if there are
* implicit gather results or if the recipient wants to send its own
@@ -938,127 +926,11 @@ public:
* be used unless they are blacklisted explicitly or unless flag 0x01
* is set.
*
- * Only a subset of this functionality is currently implemented: basic
- * path pushing and learning. Blacklisting and trust are not fully
- * implemented yet (encryption is still always used).
- *
* OK and ERROR are not generated.
*/
VERB_PUSH_DIRECT_PATHS = 0x10,
- /**
- * Source-routed circuit test message:
- * <[5] address of originator of circuit test>
- * <[2] 16-bit flags>
- * <[8] 64-bit timestamp>
- * <[8] 64-bit test ID (arbitrary, set by tester)>
- * <[2] 16-bit originator credential length (includes type)>
- * [[1] originator credential type (for authorizing test)]
- * [[...] originator credential]
- * <[2] 16-bit length of additional fields>
- * [[...] additional fields]
- * [ ... end of signed portion of request ... ]
- * <[2] 16-bit length of signature of request>
- * <[...] signature of request by originator>
- * <[2] 16-bit length of additional fields>
- * [[...] additional fields]
- * <[...] next hop(s) in path>
- *
- * Flags:
- * 0x01 - Report back to originator at all hops
- * 0x02 - Report back to originator at last hop
- *
- * Originator credential types:
- * 0x01 - 64-bit network ID for which originator is controller
- *
- * Path record format:
- * <[1] 8-bit flags (unused, must be zero)>
- * <[1] 8-bit breadth (number of next hops)>
- * <[...] one or more ZeroTier addresses of next hops>
- *
- * The circuit test allows a device to send a message that will traverse
- * the network along a specified path, with each hop optionally reporting
- * back to the tester via VERB_CIRCUIT_TEST_REPORT.
- *
- * Each circuit test packet includes a digital signature by the originator
- * of the request, as well as a credential by which that originator claims
- * authorization to perform the test. Currently this signature is ed25519,
- * but in the future flags might be used to indicate an alternative
- * algorithm. For example, the originator might be a network controller.
- * In this case the test might be authorized if the recipient is a member
- * of a network controlled by it, and if the previous hop(s) are also
- * members. Each hop may include its certificate of network membership.
- *
- * Circuit test paths consist of a series of records. When a node receives
- * an authorized circuit test, it:
- *
- * (1) Reports back to circuit tester as flags indicate
- * (2) Reads and removes the next hop from the packet's path
- * (3) Sends the packet along to next hop(s), if any.
- *
- * It is perfectly legal for a path to contain the same hop more than
- * once. In fact, this can be a very useful test to determine if a hop
- * can be reached bidirectionally and if so what that connectivity looks
- * like.
- *
- * The breadth field in source-routed path records allows a hop to forward
- * to more than one recipient, allowing the tester to specify different
- * forms of graph traversal in a test.
- *
- * There is no hard limit to the number of hops in a test, but it is
- * practically limited by the maximum size of a (possibly fragmented)
- * ZeroTier packet.
- *
- * Support for circuit tests is optional. If they are not supported, the
- * node should respond with an UNSUPPORTED_OPERATION error. If a circuit
- * test request is not authorized, it may be ignored or reported as
- * an INVALID_REQUEST. No OK messages are generated, but TEST_REPORT
- * messages may be sent (see below).
- *
- * ERROR packet format:
- * <[8] 64-bit timestamp (echoed from original>
- * <[8] 64-bit test ID (echoed from original)>
- */
- VERB_CIRCUIT_TEST = 0x11,
-
- /**
- * Circuit test hop report:
- * <[8] 64-bit timestamp (echoed from original test)>
- * <[8] 64-bit test ID (echoed from original test)>
- * <[8] 64-bit reserved field (set to 0, currently unused)>
- * <[1] 8-bit vendor ID (set to 0, currently unused)>
- * <[1] 8-bit reporter protocol version>
- * <[1] 8-bit reporter software major version>
- * <[1] 8-bit reporter software minor version>
- * <[2] 16-bit reporter software revision>
- * <[2] 16-bit reporter OS/platform or 0 if not specified>
- * <[2] 16-bit reporter architecture or 0 if not specified>
- * <[2] 16-bit error code (set to 0, currently unused)>
- * <[8] 64-bit report flags>
- * <[8] 64-bit packet ID of received CIRCUIT_TEST packet>
- * <[5] upstream ZeroTier address from which CIRCUIT_TEST was received>
- * <[1] 8-bit packet hop count of received CIRCUIT_TEST>
- * <[...] local wire address on which packet was received>
- * <[...] remote wire address from which packet was received>
- * <[2] 16-bit path link quality of path over which packet was received>
- * <[1] 8-bit number of next hops (breadth)>
- * <[...] next hop information>
- *
- * Next hop information record format:
- * <[5] ZeroTier address of next hop>
- * <[...] current best direct path address, if any, 0 if none>
- *
- * Report flags:
- * 0x1 - Upstream peer in circuit test path allowed in path (e.g. network COM valid)
- *
- * Circuit test reports can be sent by hops in a circuit test to report
- * back results. They should include information about the sender as well
- * as about the paths to which next hops are being sent.
- *
- * If a test report is received and no circuit test was sent, it should be
- * ignored. This message generates no OK or ERROR response.
- */
- VERB_CIRCUIT_TEST_REPORT = 0x12,
+ // 0x11, 0x12 -- deprecated
/**
* A message with arbitrary user-definable content:
@@ -1073,7 +945,23 @@ public:
* ZeroTier, Inc. itself. We recommend making up random ones for your own
* implementations.
*/
- VERB_USER_MESSAGE = 0x14
+ VERB_USER_MESSAGE = 0x14,
+
+ /**
+ * A trace for remote debugging or diagnostics:
+ * <[...] null-terminated dictionary containing trace information>
+ * [<[...] additional null-terminated dictionaries>]
+ *
+ * This message contains a remote trace event. Remote trace events can
+ * be sent to observers configured at the network level for those that
+ * pertain directly to actiity on a network, or to global observers if
+ * locally configured.
+ *
+ * The instance ID is a random 64-bit value generated by each ZeroTier
+ * node on startup. This is helpful in identifying traces from different
+ * members of a cluster.
+ */
+ VERB_REMOTE_TRACE = 0x15
};
/**
@@ -1109,11 +997,6 @@ public:
ERROR_UNWANTED_MULTICAST = 0x08
};
-#ifdef ZT_TRACE
- static const char *verbString(Verb v);
- static const char *errorString(ErrorCode e);
-#endif
-
template<unsigned int C2>
Packet(const Buffer<C2> &b) :
Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(b)
@@ -1320,11 +1203,6 @@ public:
inline uint64_t packetId() const { return at<uint64_t>(ZT_PACKET_IDX_IV); }
/**
- * @return Value of link quality counter extracted from this packet's ID, range 0 to 7 (3 bits)
- */
- inline unsigned int linkQualityCounter() const { return (unsigned int)(reinterpret_cast<const uint8_t *>(data())[7] & 0x07); }
-
- /**
* Set packet verb
*
* This also has the side-effect of clearing any verb flags, such as
@@ -1354,9 +1232,8 @@ public:
*
* @param key 32-byte key
* @param encryptPayload If true, encrypt packet payload, else just MAC
- * @param counter Packet send counter for destination peer -- only least significant 3 bits are used
*/
- void armor(const void *key,bool encryptPayload,unsigned int counter);
+ void armor(const void *key,bool encryptPayload);
/**
* Verify and (if encrypted) decrypt packet
@@ -1373,12 +1250,6 @@ public:
/**
* Encrypt/decrypt a separately armored portion of a packet
*
- * This currently uses Salsa20/12, but any message that uses this should
- * incorporate a cipher selector to permit this to be changed later. To
- * ensure that key stream is not reused, the key is slightly altered for
- * this use case and the same initial 32 keystream bytes that are taken
- * for MAC in ordinary armor() are also skipped here.
- *
* This is currently only used to mask portions of HELLO as an extra
* security precation since most of that message is sent in the clear.
*
diff --git a/node/Path.cpp b/node/Path.cpp
index 7366b56f..b1b3dd06 100644
--- a/node/Path.cpp
+++ b/node/Path.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include "Path.hpp"
@@ -22,9 +30,9 @@
namespace ZeroTier {
-bool Path::send(const RuntimeEnvironment *RR,void *tPtr,const void *data,unsigned int len,uint64_t now)
+bool Path::send(const RuntimeEnvironment *RR,void *tPtr,const void *data,unsigned int len,int64_t now)
{
- if (RR->node->putPacket(tPtr,_localAddress,address(),data,len)) {
+ if (RR->node->putPacket(tPtr,_localSocket,_addr,data,len)) {
_lastOut = now;
return true;
}
diff --git a/node/Path.hpp b/node/Path.hpp
index aef628d4..e12328ff 100644
--- a/node/Path.hpp
+++ b/node/Path.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_PATH_HPP
@@ -30,7 +38,6 @@
#include "InetAddress.hpp"
#include "SharedPtr.hpp"
#include "AtomicCounter.hpp"
-#include "NonCopyable.hpp"
#include "Utils.hpp"
/**
@@ -45,7 +52,7 @@ class RuntimeEnvironment;
/**
* A path across the physical network
*/
-class Path : NonCopyable
+class Path
{
friend class SharedPtr<Path>;
@@ -58,83 +65,50 @@ public:
public:
HashKey() {}
- HashKey(const InetAddress &l,const InetAddress &r)
+ HashKey(const int64_t l,const InetAddress &r)
{
- // This is an ad-hoc bit packing algorithm to yield unique keys for
- // remote addresses and their local-side counterparts if defined.
- // Portability across runtimes is not needed.
if (r.ss_family == AF_INET) {
_k[0] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&r)->sin_addr.s_addr;
_k[1] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&r)->sin_port;
- if (l.ss_family == AF_INET) {
- _k[2] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&l)->sin_addr.s_addr;
- _k[3] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&r)->sin_port;
- } else {
- _k[2] = 0;
- _k[3] = 0;
- }
+ _k[2] = (uint64_t)l;
} else if (r.ss_family == AF_INET6) {
- const uint8_t *a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr);
- uint8_t *b = reinterpret_cast<uint8_t *>(_k);
- for(unsigned int i=0;i<16;++i) b[i] = a[i];
- _k[2] = ~((uint64_t)reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_port);
- if (l.ss_family == AF_INET6) {
- _k[2] ^= ((uint64_t)reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_port) << 32;
- a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&l)->sin6_addr.s6_addr);
- b += 24;
- for(unsigned int i=0;i<8;++i) b[i] = a[i];
- a += 8;
- for(unsigned int i=0;i<8;++i) b[i] ^= a[i];
- }
+ ZT_FAST_MEMCPY(_k,reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,16);
+ _k[2] = ((uint64_t)reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_port << 32) ^ (uint64_t)l;
} else {
- _k[0] = 0;
- _k[1] = 0;
- _k[2] = 0;
- _k[3] = 0;
+ ZT_FAST_MEMCPY(_k,&r,std::min(sizeof(_k),sizeof(InetAddress)));
+ _k[2] += (uint64_t)l;
}
}
- inline unsigned long hashCode() const { return (unsigned long)(_k[0] + _k[1] + _k[2] + _k[3]); }
+ inline unsigned long hashCode() const { return (unsigned long)(_k[0] + _k[1] + _k[2]); }
- inline bool operator==(const HashKey &k) const { return ( (_k[0] == k._k[0]) && (_k[1] == k._k[1]) && (_k[2] == k._k[2]) && (_k[3] == k._k[3]) ); }
+ inline bool operator==(const HashKey &k) const { return ( (_k[0] == k._k[0]) && (_k[1] == k._k[1]) && (_k[2] == k._k[2]) ); }
inline bool operator!=(const HashKey &k) const { return (!(*this == k)); }
private:
- uint64_t _k[4];
+ uint64_t _k[3];
};
Path() :
_lastOut(0),
_lastIn(0),
_lastTrustEstablishedPacketReceived(0),
- _incomingLinkQualityFastLog(0xffffffffffffffffULL),
- _incomingLinkQualitySlowLogPtr(0),
- _incomingLinkQualitySlowLogCounter(-64), // discard first fast log
- _incomingLinkQualityPreviousPacketCounter(0),
- _outgoingPacketCounter(0),
+ _localSocket(-1),
+ _latency(0xffff),
_addr(),
- _localAddress(),
_ipScope(InetAddress::IP_SCOPE_NONE)
{
- for(int i=0;i<(int)sizeof(_incomingLinkQualitySlowLog);++i)
- _incomingLinkQualitySlowLog[i] = ZT_PATH_LINK_QUALITY_MAX;
}
- Path(const InetAddress &localAddress,const InetAddress &addr) :
+ Path(const int64_t localSocket,const InetAddress &addr) :
_lastOut(0),
_lastIn(0),
_lastTrustEstablishedPacketReceived(0),
- _incomingLinkQualityFastLog(0xffffffffffffffffULL),
- _incomingLinkQualitySlowLogPtr(0),
- _incomingLinkQualitySlowLogCounter(-64), // discard first fast log
- _incomingLinkQualityPreviousPacketCounter(0),
- _outgoingPacketCounter(0),
+ _localSocket(localSocket),
+ _latency(0xffff),
_addr(addr),
- _localAddress(localAddress),
_ipScope(addr.ipScope())
{
- for(int i=0;i<(int)sizeof(_incomingLinkQualitySlowLog);++i)
- _incomingLinkQualitySlowLog[i] = ZT_PATH_LINK_QUALITY_MAX;
}
/**
@@ -145,39 +119,6 @@ public:
inline void received(const uint64_t t) { _lastIn = t; }
/**
- * Update link quality using a counter from an incoming packet (or packet head in fragmented case)
- *
- * @param counter Packet link quality counter (range 0 to 7, must not have other bits set)
- */
- inline void updateLinkQuality(const unsigned int counter)
- {
- const unsigned int prev = _incomingLinkQualityPreviousPacketCounter;
- _incomingLinkQualityPreviousPacketCounter = counter;
- const uint64_t fl = (_incomingLinkQualityFastLog = ((_incomingLinkQualityFastLog << 1) | (uint64_t)(prev == ((counter - 1) & 0x7))));
- if (++_incomingLinkQualitySlowLogCounter >= 64) {
- _incomingLinkQualitySlowLogCounter = 0;
- _incomingLinkQualitySlowLog[_incomingLinkQualitySlowLogPtr++ % sizeof(_incomingLinkQualitySlowLog)] = (uint8_t)Utils::countBits(fl);
- }
- }
-
- /**
- * @return Link quality from 0 (min) to 255 (max)
- */
- inline unsigned int linkQuality() const
- {
- unsigned long slsize = _incomingLinkQualitySlowLogPtr;
- if (slsize > (unsigned long)sizeof(_incomingLinkQualitySlowLog))
- slsize = (unsigned long)sizeof(_incomingLinkQualitySlowLog);
- else if (!slsize)
- return 255; // ZT_PATH_LINK_QUALITY_MAX
- unsigned long lq = 0;
- for(unsigned long i=0;i<slsize;++i)
- lq += (unsigned long)_incomingLinkQualitySlowLog[i] * 4;
- lq /= slsize;
- return (unsigned int)((lq >= 255) ? 255 : lq);
- }
-
- /**
* Set time last trusted packet was received (done in Peer::received())
*/
inline void trustedPacketReceived(const uint64_t t) { _lastTrustEstablishedPacketReceived = t; }
@@ -192,19 +133,32 @@ public:
* @param now Current time
* @return True if transport reported success
*/
- bool send(const RuntimeEnvironment *RR,void *tPtr,const void *data,unsigned int len,uint64_t now);
+ bool send(const RuntimeEnvironment *RR,void *tPtr,const void *data,unsigned int len,int64_t now);
/**
* Manually update last sent time
*
* @param t Time of send
*/
- inline void sent(const uint64_t t) { _lastOut = t; }
+ inline void sent(const int64_t t) { _lastOut = t; }
/**
- * @return Address of local side of this path or NULL if unspecified
+ * Update path latency with a new measurement
+ *
+ * @param l Measured latency
*/
- inline const InetAddress &localAddress() const { return _localAddress; }
+ inline void updateLatency(const unsigned int l)
+ {
+ unsigned int pl = _latency;
+ if (pl < 0xffff)
+ _latency = (pl + l) / 2;
+ else _latency = l;
+ }
+
+ /**
+ * @return Local socket as specified by external code
+ */
+ inline int64_t localSocket() const { return _localSocket; }
/**
* @return Physical address
@@ -219,7 +173,7 @@ public:
/**
* @return True if path has received a trust established packet (e.g. common network membership) in the past ZT_TRUST_EXPIRATION ms
*/
- inline bool trustEstablished(const uint64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); }
+ inline bool trustEstablished(const int64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); }
/**
* @return Preference rank, higher == better
@@ -272,45 +226,53 @@ public:
}
/**
- * @return True if path appears alive
+ * @return Latency or 0xffff if unknown
+ */
+ inline unsigned int latency() const { return _latency; }
+
+ /**
+ * @return Path quality -- lower is better
+ */
+ inline long quality(const int64_t now) const
+ {
+ const int l = (long)_latency;
+ const int age = (long)std::min((now - _lastIn),(int64_t)(ZT_PATH_HEARTBEAT_PERIOD * 10)); // set an upper sanity limit to avoid overflow
+ return (((age < (ZT_PATH_HEARTBEAT_PERIOD + 5000)) ? l : (l + 0xffff + age)) * (long)((ZT_INETADDRESS_MAX_SCOPE - _ipScope) + 1));
+ }
+
+ /**
+ * @return True if this path is alive (receiving heartbeats)
*/
- inline bool alive(const uint64_t now) const { return ((now - _lastIn) <= ZT_PATH_ALIVE_TIMEOUT); }
+ inline bool alive(const int64_t now) const { return ((now - _lastIn) < (ZT_PATH_HEARTBEAT_PERIOD + 5000)); }
/**
* @return True if this path needs a heartbeat
*/
- inline bool needsHeartbeat(const uint64_t now) const { return ((now - _lastOut) >= ZT_PATH_HEARTBEAT_PERIOD); }
+ inline bool needsHeartbeat(const int64_t now) const { return ((now - _lastOut) >= ZT_PATH_HEARTBEAT_PERIOD); }
/**
* @return Last time we sent something
*/
- inline uint64_t lastOut() const { return _lastOut; }
+ inline int64_t lastOut() const { return _lastOut; }
/**
* @return Last time we received anything
*/
- inline uint64_t lastIn() const { return _lastIn; }
+ inline int64_t lastIn() const { return _lastIn; }
/**
- * Return and increment outgoing packet counter (used with Packet::armor())
- *
- * @return Next value that should be used for outgoing packet counter (only least significant 3 bits are used)
+ * @return Time last trust-established packet was received
*/
- inline unsigned int nextOutgoingCounter() { return _outgoingPacketCounter++; }
+ inline int64_t lastTrustEstablishedPacketReceived() const { return _lastTrustEstablishedPacketReceived; }
private:
- volatile uint64_t _lastOut;
- volatile uint64_t _lastIn;
- volatile uint64_t _lastTrustEstablishedPacketReceived;
- volatile uint64_t _incomingLinkQualityFastLog;
- volatile unsigned long _incomingLinkQualitySlowLogPtr;
- volatile signed int _incomingLinkQualitySlowLogCounter;
- volatile unsigned int _incomingLinkQualityPreviousPacketCounter;
- volatile unsigned int _outgoingPacketCounter;
+ volatile int64_t _lastOut;
+ volatile int64_t _lastIn;
+ volatile int64_t _lastTrustEstablishedPacketReceived;
+ int64_t _localSocket;
+ volatile unsigned int _latency;
InetAddress _addr;
- InetAddress _localAddress;
InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often
- volatile uint8_t _incomingLinkQualitySlowLog[32];
AtomicCounter __refCount;
};
diff --git a/node/Peer.cpp b/node/Peer.cpp
index 2e9f6a2b..71afd852 100644
--- a/node/Peer.cpp
+++ b/node/Peer.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include "../version.h"
@@ -24,8 +32,9 @@
#include "Switch.hpp"
#include "Network.hpp"
#include "SelfAwareness.hpp"
-#include "Cluster.hpp"
#include "Packet.hpp"
+#include "Trace.hpp"
+#include "InetAddress.hpp"
namespace ZeroTier {
@@ -43,17 +52,17 @@ Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Ident
_lastComRequestSent(0),
_lastCredentialsReceived(0),
_lastTrustEstablishedPacketReceived(0),
+ _lastSentFullHello(0),
_vProto(0),
_vMajor(0),
_vMinor(0),
_vRevision(0),
_id(peerIdentity),
- _latency(0),
_directPathPushCutoffCount(0),
_credentialsCutoffCount(0)
{
if (!myIdentity.agree(peerIdentity,_key,ZT_PEER_SECRET_KEY_LENGTH))
- throw std::runtime_error("new peer identity key agreement failed");
+ throw ZT_EXCEPTION_INVALID_ARGUMENT;
}
void Peer::received(
@@ -64,55 +73,10 @@ void Peer::received(
const Packet::Verb verb,
const uint64_t inRePacketId,
const Packet::Verb inReVerb,
- const bool trustEstablished)
+ const bool trustEstablished,
+ const uint64_t networkId)
{
- const uint64_t now = RR->node->now();
-
-#ifdef ZT_ENABLE_CLUSTER
- bool isClusterSuboptimalPath = false;
- if ((RR->cluster)&&(hops == 0)) {
- // Note: findBetterEndpoint() is first since we still want to check
- // for a better endpoint even if we don't actually send a redirect.
- InetAddress redirectTo;
- if ( (verb != Packet::VERB_OK) && (verb != Packet::VERB_ERROR) && (verb != Packet::VERB_RENDEZVOUS) && (verb != Packet::VERB_PUSH_DIRECT_PATHS) && (RR->cluster->findBetterEndpoint(redirectTo,_id.address(),path->address(),false)) ) {
- if (_vProto >= 5) {
- // For newer peers we can send a more idiomatic verb: PUSH_DIRECT_PATHS.
- Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS);
- outp.append((uint16_t)1); // count == 1
- outp.append((uint8_t)ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT); // flags: cluster redirect
- outp.append((uint16_t)0); // no extensions
- if (redirectTo.ss_family == AF_INET) {
- outp.append((uint8_t)4);
- outp.append((uint8_t)6);
- outp.append(redirectTo.rawIpData(),4);
- } else {
- outp.append((uint8_t)6);
- outp.append((uint8_t)18);
- outp.append(redirectTo.rawIpData(),16);
- }
- outp.append((uint16_t)redirectTo.port());
- outp.armor(_key,true,path->nextOutgoingCounter());
- path->send(RR,tPtr,outp.data(),outp.size(),now);
- } else {
- // For older peers we use RENDEZVOUS to coax them into contacting us elsewhere.
- Packet outp(_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS);
- outp.append((uint8_t)0); // no flags
- RR->identity.address().appendTo(outp);
- outp.append((uint16_t)redirectTo.port());
- if (redirectTo.ss_family == AF_INET) {
- outp.append((uint8_t)4);
- outp.append(redirectTo.rawIpData(),4);
- } else {
- outp.append((uint8_t)16);
- outp.append(redirectTo.rawIpData(),16);
- }
- outp.armor(_key,true,path->nextOutgoingCounter());
- path->send(RR,tPtr,outp.data(),outp.size(),now);
- }
- isClusterSuboptimalPath = true;
- }
- }
-#endif
+ const int64_t now = RR->node->now();
_lastReceive = now;
switch (verb) {
@@ -131,77 +95,80 @@ void Peer::received(
path->trustedPacketReceived(now);
}
- if (_vProto >= 9)
- path->updateLinkQuality((unsigned int)(packetId & 7));
-
if (hops == 0) {
- bool pathAlreadyKnown = false;
+ // If this is a direct packet (no hops), update existing paths or learn new ones
+
+ bool havePath = false;
{
Mutex::Lock _l(_paths_m);
- if ((path->address().ss_family == AF_INET)&&(_v4Path.p)) {
- const struct sockaddr_in *const r = reinterpret_cast<const struct sockaddr_in *>(&(path->address()));
- const struct sockaddr_in *const l = reinterpret_cast<const struct sockaddr_in *>(&(_v4Path.p->address()));
- const struct sockaddr_in *const rl = reinterpret_cast<const struct sockaddr_in *>(&(path->localAddress()));
- const struct sockaddr_in *const ll = reinterpret_cast<const struct sockaddr_in *>(&(_v4Path.p->localAddress()));
- if ((r->sin_addr.s_addr == l->sin_addr.s_addr)&&(r->sin_port == l->sin_port)&&(rl->sin_addr.s_addr == ll->sin_addr.s_addr)&&(rl->sin_port == ll->sin_port)) {
- _v4Path.lr = now;
-#ifdef ZT_ENABLE_CLUSTER
- _v4Path.localClusterSuboptimal = isClusterSuboptimalPath;
-#endif
- pathAlreadyKnown = true;
- }
- } else if ((path->address().ss_family == AF_INET6)&&(_v6Path.p)) {
- const struct sockaddr_in6 *const r = reinterpret_cast<const struct sockaddr_in6 *>(&(path->address()));
- const struct sockaddr_in6 *const l = reinterpret_cast<const struct sockaddr_in6 *>(&(_v6Path.p->address()));
- const struct sockaddr_in6 *const rl = reinterpret_cast<const struct sockaddr_in6 *>(&(path->localAddress()));
- const struct sockaddr_in6 *const ll = reinterpret_cast<const struct sockaddr_in6 *>(&(_v6Path.p->localAddress()));
- if ((!memcmp(r->sin6_addr.s6_addr,l->sin6_addr.s6_addr,16))&&(r->sin6_port == l->sin6_port)&&(!memcmp(rl->sin6_addr.s6_addr,ll->sin6_addr.s6_addr,16))&&(rl->sin6_port == ll->sin6_port)) {
- _v6Path.lr = now;
-#ifdef ZT_ENABLE_CLUSTER
- _v6Path.localClusterSuboptimal = isClusterSuboptimalPath;
-#endif
- pathAlreadyKnown = true;
- }
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ if (_paths[i].p == path) {
+ _paths[i].lr = now;
+ havePath = true;
+ break;
+ }
+ } else break;
}
}
- if ( (!pathAlreadyKnown) && (RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id.address(),path->localAddress(),path->address())) ) {
+ bool attemptToContact = false;
+ if ((!havePath)&&(RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id.address(),path->localSocket(),path->address()))) {
Mutex::Lock _l(_paths_m);
- _PeerPath *potentialNewPeerPath = (_PeerPath *)0;
- if (path->address().ss_family == AF_INET) {
- if ( (!_v4Path.p) || (!_v4Path.p->alive(now)) || ((_v4Path.p->address() != _v4ClusterPreferred)&&(path->preferenceRank() >= _v4Path.p->preferenceRank())) ) {
- potentialNewPeerPath = &_v4Path;
- }
- } else if (path->address().ss_family == AF_INET6) {
- if ( (!_v6Path.p) || (!_v6Path.p->alive(now)) || ((_v6Path.p->address() != _v6ClusterPreferred)&&(path->preferenceRank() >= _v6Path.p->preferenceRank())) ) {
- potentialNewPeerPath = &_v6Path;
- }
+
+ // Paths are redunant if they duplicate an alive path to the same IP or
+ // with the same local socket and address family.
+ bool redundant = false;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ if ( (_paths[i].p->alive(now)) && ( ((_paths[i].p->localSocket() == path->localSocket())&&(_paths[i].p->address().ss_family == path->address().ss_family)) || (_paths[i].p->address().ipsEqual2(path->address())) ) ) {
+ redundant = true;
+ break;
+ }
+ } else break;
}
- if (potentialNewPeerPath) {
- if (verb == Packet::VERB_OK) {
- potentialNewPeerPath->lr = now;
- potentialNewPeerPath->p = path;
-#ifdef ZT_ENABLE_CLUSTER
- potentialNewPeerPath->localClusterSuboptimal = isClusterSuboptimalPath;
- if (RR->cluster)
- RR->cluster->broadcastHavePeer(_id);
-#endif
- } else {
- TRACE("got %s via unknown path %s(%s), confirming...",Packet::verbString(verb),_id.address().toString().c_str(),path->address().toString().c_str());
- attemptToContactAt(tPtr,path->localAddress(),path->address(),now,true,path->nextOutgoingCounter());
- path->sent(now);
+
+ if (!redundant) {
+ unsigned int replacePath = ZT_MAX_PEER_NETWORK_PATHS;
+ int replacePathQuality = 0;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ const int q = _paths[i].p->quality(now);
+ if (q > replacePathQuality) {
+ replacePathQuality = q;
+ replacePath = i;
+ }
+ } else {
+ replacePath = i;
+ break;
+ }
+ }
+
+ if (replacePath != ZT_MAX_PEER_NETWORK_PATHS) {
+ if (verb == Packet::VERB_OK) {
+ RR->t->peerLearnedNewPath(tPtr,networkId,*this,path,packetId);
+ _paths[replacePath].lr = now;
+ _paths[replacePath].p = path;
+ _paths[replacePath].priority = 1;
+ } else {
+ attemptToContact = true;
+ }
}
}
}
- } else if (this->trustEstablished(now)) {
- // Send PUSH_DIRECT_PATHS if hops>0 (relayed) and we have a trust relationship (common network membership)
-#ifdef ZT_ENABLE_CLUSTER
- // Cluster mode disables normal PUSH_DIRECT_PATHS in favor of cluster-based peer redirection
- const bool haveCluster = (RR->cluster);
-#else
- const bool haveCluster = false;
-#endif
- if ( ((now - _lastDirectPathPushSent) >= ZT_DIRECT_PATH_PUSH_INTERVAL) && (!haveCluster) ) {
+
+ if (attemptToContact) {
+ attemptToContactAt(tPtr,path->localSocket(),path->address(),now,true);
+ path->sent(now);
+ RR->t->peerConfirmingUnknownPath(tPtr,networkId,*this,path,packetId,verb);
+ }
+ }
+
+ // If we have a trust relationship periodically push a message enumerating
+ // all known external addresses for ourselves. We now do this even if we
+ // have a current path since we'll want to use new ones too.
+ if (this->trustEstablished(now)) {
+ if ((now - _lastDirectPathPushSent) >= ZT_DIRECT_PATH_PUSH_INTERVAL) {
_lastDirectPathPushSent = now;
std::vector<InetAddress> pathsToPush;
@@ -210,27 +177,20 @@ void Peer::received(
for(std::vector<InetAddress>::const_iterator i(dps.begin());i!=dps.end();++i)
pathsToPush.push_back(*i);
- std::vector<InetAddress> sym(RR->sa->getSymmetricNatPredictions());
- for(unsigned long i=0,added=0;i<sym.size();++i) {
- InetAddress tmp(sym[(unsigned long)RR->node->prng() % sym.size()]);
- if (std::find(pathsToPush.begin(),pathsToPush.end(),tmp) == pathsToPush.end()) {
- pathsToPush.push_back(tmp);
- if (++added >= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY)
- break;
+ // Do symmetric NAT prediction if we are communicating indirectly.
+ if (hops > 0) {
+ std::vector<InetAddress> sym(RR->sa->getSymmetricNatPredictions());
+ for(unsigned long i=0,added=0;i<sym.size();++i) {
+ InetAddress tmp(sym[(unsigned long)RR->node->prng() % sym.size()]);
+ if (std::find(pathsToPush.begin(),pathsToPush.end(),tmp) == pathsToPush.end()) {
+ pathsToPush.push_back(tmp);
+ if (++added >= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY)
+ break;
+ }
}
}
if (pathsToPush.size() > 0) {
-#ifdef ZT_TRACE
- std::string ps;
- for(std::vector<InetAddress>::const_iterator p(pathsToPush.begin());p!=pathsToPush.end();++p) {
- if (ps.length() > 0)
- ps.push_back(',');
- ps.append(p->toString());
- }
- TRACE("pushing %u direct paths to %s: %s",(unsigned int)pathsToPush.size(),_id.address().toString().c_str(),ps.c_str());
-#endif
-
std::vector<InetAddress>::const_iterator p(pathsToPush.begin());
while (p != pathsToPush.end()) {
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS);
@@ -263,7 +223,7 @@ void Peer::received(
if (count) {
outp.setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count);
- outp.armor(_key,true,path->nextOutgoingCounter());
+ outp.armor(_key,true);
path->send(RR,tPtr,outp.data(),outp.size(),now);
}
}
@@ -272,53 +232,151 @@ void Peer::received(
}
}
-bool Peer::sendDirect(void *tPtr,const void *data,unsigned int len,uint64_t now,bool force)
+SharedPtr<Path> Peer::getBestPath(int64_t now,bool includeExpired) const
{
Mutex::Lock _l(_paths_m);
- uint64_t v6lr = 0;
- if ( ((now - _v6Path.lr) < ZT_PEER_PATH_EXPIRATION) && (_v6Path.p) )
- v6lr = _v6Path.p->lastIn();
- uint64_t v4lr = 0;
- if ( ((now - _v4Path.lr) < ZT_PEER_PATH_EXPIRATION) && (_v4Path.p) )
- v4lr = _v4Path.p->lastIn();
-
- if ( (v6lr > v4lr) && ((now - v6lr) < ZT_PATH_ALIVE_TIMEOUT) ) {
- return _v6Path.p->send(RR,tPtr,data,len,now);
- } else if ((now - v4lr) < ZT_PATH_ALIVE_TIMEOUT) {
- return _v4Path.p->send(RR,tPtr,data,len,now);
- } else if (force) {
- if (v6lr > v4lr) {
- return _v6Path.p->send(RR,tPtr,data,len,now);
- } else if (v4lr) {
- return _v4Path.p->send(RR,tPtr,data,len,now);
- }
+ unsigned int bestPath = ZT_MAX_PEER_NETWORK_PATHS;
+ long bestPathQuality = 2147483647;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ if ((includeExpired)||((now - _paths[i].lr) < ZT_PEER_PATH_EXPIRATION)) {
+ const long q = _paths[i].p->quality(now) / _paths[i].priority;
+ if (q <= bestPathQuality) {
+ bestPathQuality = q;
+ bestPath = i;
+ }
+ }
+ } else break;
}
- return false;
+ if (bestPath != ZT_MAX_PEER_NETWORK_PATHS)
+ return _paths[bestPath].p;
+ return SharedPtr<Path>();
}
-SharedPtr<Path> Peer::getBestPath(uint64_t now,bool includeExpired)
+void Peer::introduce(void *const tPtr,const int64_t now,const SharedPtr<Peer> &other) const
{
- Mutex::Lock _l(_paths_m);
+ unsigned int myBestV4ByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ unsigned int myBestV6ByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ long myBestV4QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ long myBestV6QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ unsigned int theirBestV4ByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ unsigned int theirBestV6ByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ long theirBestV4QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ long theirBestV6QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ for(int i=0;i<=ZT_INETADDRESS_MAX_SCOPE;++i) {
+ myBestV4ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS;
+ myBestV6ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS;
+ myBestV4QualityByScope[i] = 2147483647;
+ myBestV6QualityByScope[i] = 2147483647;
+ theirBestV4ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS;
+ theirBestV6ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS;
+ theirBestV4QualityByScope[i] = 2147483647;
+ theirBestV6QualityByScope[i] = 2147483647;
+ }
+
+ Mutex::Lock _l1(_paths_m);
+
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ const long q = _paths[i].p->quality(now) / _paths[i].priority;
+ const unsigned int s = (unsigned int)_paths[i].p->ipScope();
+ switch(_paths[i].p->address().ss_family) {
+ case AF_INET:
+ if (q <= myBestV4QualityByScope[s]) {
+ myBestV4QualityByScope[s] = q;
+ myBestV4ByScope[s] = i;
+ }
+ break;
+ case AF_INET6:
+ if (q <= myBestV6QualityByScope[s]) {
+ myBestV6QualityByScope[s] = q;
+ myBestV6ByScope[s] = i;
+ }
+ break;
+ }
+ } else break;
+ }
- uint64_t v6lr = 0;
- if ( ( includeExpired || ((now - _v6Path.lr) < ZT_PEER_PATH_EXPIRATION) ) && (_v6Path.p) )
- v6lr = _v6Path.p->lastIn();
- uint64_t v4lr = 0;
- if ( ( includeExpired || ((now - _v4Path.lr) < ZT_PEER_PATH_EXPIRATION) ) && (_v4Path.p) )
- v4lr = _v4Path.p->lastIn();
-
- if (v6lr > v4lr) {
- return _v6Path.p;
- } else if (v4lr) {
- return _v4Path.p;
+ Mutex::Lock _l2(other->_paths_m);
+
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (other->_paths[i].p) {
+ const long q = other->_paths[i].p->quality(now) / other->_paths[i].priority;
+ const unsigned int s = (unsigned int)other->_paths[i].p->ipScope();
+ switch(other->_paths[i].p->address().ss_family) {
+ case AF_INET:
+ if (q <= theirBestV4QualityByScope[s]) {
+ theirBestV4QualityByScope[s] = q;
+ theirBestV4ByScope[s] = i;
+ }
+ break;
+ case AF_INET6:
+ if (q <= theirBestV6QualityByScope[s]) {
+ theirBestV6QualityByScope[s] = q;
+ theirBestV6ByScope[s] = i;
+ }
+ break;
+ }
+ } else break;
}
- return SharedPtr<Path>();
+ unsigned int mine = ZT_MAX_PEER_NETWORK_PATHS;
+ unsigned int theirs = ZT_MAX_PEER_NETWORK_PATHS;
+
+ for(int s=ZT_INETADDRESS_MAX_SCOPE;s>=0;--s) {
+ if ((myBestV6ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)&&(theirBestV6ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)) {
+ mine = myBestV6ByScope[s];
+ theirs = theirBestV6ByScope[s];
+ break;
+ }
+ if ((myBestV4ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)&&(theirBestV4ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)) {
+ mine = myBestV4ByScope[s];
+ theirs = theirBestV4ByScope[s];
+ break;
+ }
+ }
+
+ if (mine != ZT_MAX_PEER_NETWORK_PATHS) {
+ unsigned int alt = (unsigned int)RR->node->prng() & 1; // randomize which hint we send first for black magickal NAT-t reasons
+ const unsigned int completed = alt + 2;
+ while (alt != completed) {
+ if ((alt & 1) == 0) {
+ Packet outp(_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS);
+ outp.append((uint8_t)0);
+ other->_id.address().appendTo(outp);
+ outp.append((uint16_t)other->_paths[theirs].p->address().port());
+ if (other->_paths[theirs].p->address().ss_family == AF_INET6) {
+ outp.append((uint8_t)16);
+ outp.append(other->_paths[theirs].p->address().rawIpData(),16);
+ } else {
+ outp.append((uint8_t)4);
+ outp.append(other->_paths[theirs].p->address().rawIpData(),4);
+ }
+ outp.armor(_key,true);
+ _paths[mine].p->send(RR,tPtr,outp.data(),outp.size(),now);
+ } else {
+ Packet outp(other->_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS);
+ outp.append((uint8_t)0);
+ _id.address().appendTo(outp);
+ outp.append((uint16_t)_paths[mine].p->address().port());
+ if (_paths[mine].p->address().ss_family == AF_INET6) {
+ outp.append((uint8_t)16);
+ outp.append(_paths[mine].p->address().rawIpData(),16);
+ } else {
+ outp.append((uint8_t)4);
+ outp.append(_paths[mine].p->address().rawIpData(),4);
+ }
+ outp.armor(other->_key,true);
+ other->_paths[theirs].p->send(RR,tPtr,outp.data(),outp.size(),now);
+ }
+ ++alt;
+ }
+ }
}
-void Peer::sendHELLO(void *tPtr,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int counter)
+void Peer::sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now)
{
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_HELLO);
@@ -349,87 +407,147 @@ void Peer::sendHELLO(void *tPtr,const InetAddress &localAddr,const InetAddress &
outp.append((uint64_t)0);
}
- const unsigned int corSizeAt = outp.size();
- outp.addSize(2);
- RR->topology->appendCertificateOfRepresentation(outp);
- outp.setAt(corSizeAt,(uint16_t)(outp.size() - (corSizeAt + 2)));
-
outp.cryptField(_key,startCryptedPortionAt,outp.size() - startCryptedPortionAt);
RR->node->expectReplyTo(outp.packetId());
if (atAddress) {
- outp.armor(_key,false,counter); // false == don't encrypt full payload, but add MAC
- RR->node->putPacket(tPtr,localAddr,atAddress,outp.data(),outp.size());
+ outp.armor(_key,false); // false == don't encrypt full payload, but add MAC
+ RR->node->putPacket(tPtr,localSocket,atAddress,outp.data(),outp.size());
} else {
RR->sw->send(tPtr,outp,false); // false == don't encrypt full payload, but add MAC
}
}
-void Peer::attemptToContactAt(void *tPtr,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,bool sendFullHello,unsigned int counter)
+void Peer::attemptToContactAt(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now,bool sendFullHello)
{
if ( (!sendFullHello) && (_vProto >= 5) && (!((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0))) ) {
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
RR->node->expectReplyTo(outp.packetId());
- outp.armor(_key,true,counter);
- RR->node->putPacket(tPtr,localAddr,atAddress,outp.data(),outp.size());
+ outp.armor(_key,true);
+ RR->node->putPacket(tPtr,localSocket,atAddress,outp.data(),outp.size());
} else {
- sendHELLO(tPtr,localAddr,atAddress,now,counter);
+ sendHELLO(tPtr,localSocket,atAddress,now);
}
}
-void Peer::tryMemorizedPath(void *tPtr,uint64_t now)
+void Peer::tryMemorizedPath(void *tPtr,int64_t now)
{
if ((now - _lastTriedMemorizedPath) >= ZT_TRY_MEMORIZED_PATH_INTERVAL) {
_lastTriedMemorizedPath = now;
InetAddress mp;
if (RR->node->externalPathLookup(tPtr,_id.address(),-1,mp))
- attemptToContactAt(tPtr,InetAddress(),mp,now,true,0);
+ attemptToContactAt(tPtr,-1,mp,now,true);
}
}
-bool Peer::doPingAndKeepalive(void *tPtr,uint64_t now,int inetAddressFamily)
+unsigned int Peer::doPingAndKeepalive(void *tPtr,int64_t now)
{
+ unsigned int sent = 0;
+
Mutex::Lock _l(_paths_m);
- if (inetAddressFamily < 0) {
- uint64_t v6lr = 0;
- if ( ((now - _v6Path.lr) < ZT_PEER_PATH_EXPIRATION) && (_v6Path.p) )
- v6lr = _v6Path.p->lastIn();
- uint64_t v4lr = 0;
- if ( ((now - _v4Path.lr) < ZT_PEER_PATH_EXPIRATION) && (_v4Path.p) )
- v4lr = _v4Path.p->lastIn();
-
- if (v6lr > v4lr) {
- if ( ((now - _v6Path.lr) >= ZT_PEER_PING_PERIOD) || (_v6Path.p->needsHeartbeat(now)) ) {
- attemptToContactAt(tPtr,_v6Path.p->localAddress(),_v6Path.p->address(),now,false,_v6Path.p->nextOutgoingCounter());
- _v6Path.p->sent(now);
- return true;
- }
- } else if (v4lr) {
- if ( ((now - _v4Path.lr) >= ZT_PEER_PING_PERIOD) || (_v4Path.p->needsHeartbeat(now)) ) {
- attemptToContactAt(tPtr,_v4Path.p->localAddress(),_v4Path.p->address(),now,false,_v4Path.p->nextOutgoingCounter());
- _v4Path.p->sent(now);
- return true;
+ const bool sendFullHello = ((now - _lastSentFullHello) >= ZT_PEER_PING_PERIOD);
+ _lastSentFullHello = now;
+
+ // Right now we only keep pinging links that have the maximum priority. The
+ // priority is used to track cluster redirections, meaning that when a cluster
+ // redirects us its redirect target links override all other links and we
+ // let those old links expire.
+ long maxPriority = 0;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p)
+ maxPriority = std::max(_paths[i].priority,maxPriority);
+ else break;
+ }
+
+ unsigned int j = 0;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ // Clean expired and reduced priority paths
+ if ( ((now - _paths[i].lr) < ZT_PEER_PATH_EXPIRATION) && (_paths[i].priority == maxPriority) ) {
+ if ((sendFullHello)||(_paths[i].p->needsHeartbeat(now))) {
+ attemptToContactAt(tPtr,_paths[i].p->localSocket(),_paths[i].p->address(),now,sendFullHello);
+ _paths[i].p->sent(now);
+ sent |= (_paths[i].p->address().ss_family == AF_INET) ? 0x1 : 0x2;
+ }
+ if (i != j)
+ _paths[j] = _paths[i];
+ ++j;
}
+ } else break;
+ }
+ while(j < ZT_MAX_PEER_NETWORK_PATHS) {
+ _paths[j].lr = 0;
+ _paths[j].p.zero();
+ _paths[j].priority = 1;
+ ++j;
+ }
+
+ return sent;
+}
+
+void Peer::clusterRedirect(void *tPtr,const SharedPtr<Path> &originatingPath,const InetAddress &remoteAddress,const int64_t now)
+{
+ SharedPtr<Path> np(RR->topology->getPath(originatingPath->localSocket(),remoteAddress));
+ RR->t->peerRedirected(tPtr,0,*this,np);
+
+ attemptToContactAt(tPtr,originatingPath->localSocket(),remoteAddress,now,true);
+
+ {
+ Mutex::Lock _l(_paths_m);
+
+ // New priority is higher than the priority of the originating path (if known)
+ long newPriority = 1;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ if (_paths[i].p == originatingPath) {
+ newPriority = _paths[i].priority;
+ break;
+ }
+ } else break;
}
- } else {
- if ( (inetAddressFamily == AF_INET) && ((now - _v4Path.lr) < ZT_PEER_PATH_EXPIRATION) ) {
- if ( ((now - _v4Path.lr) >= ZT_PEER_PING_PERIOD) || (_v4Path.p->needsHeartbeat(now)) ) {
- attemptToContactAt(tPtr,_v4Path.p->localAddress(),_v4Path.p->address(),now,false,_v4Path.p->nextOutgoingCounter());
- _v4Path.p->sent(now);
- return true;
+ newPriority += 2;
+
+ // Erase any paths with lower priority than this one or that are duplicate
+ // IPs and add this path.
+ unsigned int j = 0;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ if ((_paths[i].priority >= newPriority)&&(!_paths[i].p->address().ipsEqual2(remoteAddress))) {
+ if (i != j)
+ _paths[j] = _paths[i];
+ ++j;
+ }
}
- } else if ( (inetAddressFamily == AF_INET6) && ((now - _v6Path.lr) < ZT_PEER_PATH_EXPIRATION) ) {
- if ( ((now - _v6Path.lr) >= ZT_PEER_PING_PERIOD) || (_v6Path.p->needsHeartbeat(now)) ) {
- attemptToContactAt(tPtr,_v6Path.p->localAddress(),_v6Path.p->address(),now,false,_v6Path.p->nextOutgoingCounter());
- _v6Path.p->sent(now);
- return true;
+ }
+ if (j < ZT_MAX_PEER_NETWORK_PATHS) {
+ _paths[j].lr = now;
+ _paths[j].p = np;
+ _paths[j].priority = newPriority;
+ ++j;
+ while (j < ZT_MAX_PEER_NETWORK_PATHS) {
+ _paths[j].lr = 0;
+ _paths[j].p.zero();
+ _paths[j].priority = 1;
+ ++j;
}
}
}
+}
- return false;
+void Peer::resetWithinScope(void *tPtr,InetAddress::IpScope scope,int inetAddressFamily,int64_t now)
+{
+ Mutex::Lock _l(_paths_m);
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ if ((_paths[i].p->address().ss_family == inetAddressFamily)&&(_paths[i].p->ipScope() == scope)) {
+ attemptToContactAt(tPtr,_paths[i].p->localSocket(),_paths[i].p->address(),now,false);
+ _paths[i].p->sent(now);
+ _paths[i].lr = 0; // path will not be used unless it speaks again
+ }
+ } else break;
+ }
}
} // namespace ZeroTier
diff --git a/node/Peer.hpp b/node/Peer.hpp
index b9d85404..b6f3c695 100644
--- a/node/Peer.hpp
+++ b/node/Peer.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_PEER_HPP
@@ -41,14 +49,15 @@
#include "AtomicCounter.hpp"
#include "Hashtable.hpp"
#include "Mutex.hpp"
-#include "NonCopyable.hpp"
+
+#define ZT_PEER_MAX_SERIALIZED_STATE_SIZE (sizeof(Peer) + 32 + (sizeof(Path) * 2))
namespace ZeroTier {
/**
* Peer on P2P Network (virtual layer 1)
*/
-class Peer : NonCopyable
+class Peer
{
friend class SharedPtr<Peer>;
@@ -71,12 +80,12 @@ public:
/**
* @return This peer's ZT address (short for identity().address())
*/
- inline const Address &address() const throw() { return _id.address(); }
+ inline const Address &address() const { return _id.address(); }
/**
* @return This peer's identity
*/
- inline const Identity &identity() const throw() { return _id; }
+ inline const Identity &identity() const { return _id; }
/**
* Log receipt of an authenticated packet
@@ -92,6 +101,7 @@ public:
* @param inRePacketId Packet ID in reply to (default: none)
* @param inReVerb Verb in reply to (for OK/ERROR, default: VERB_NOP)
* @param trustEstablished If true, some form of non-trivial trust (like allowed in network) has been established
+ * @param networkId Network ID if this pertains to a network, or 0 otherwise
*/
void received(
void *tPtr,
@@ -101,17 +111,26 @@ public:
const Packet::Verb verb,
const uint64_t inRePacketId,
const Packet::Verb inReVerb,
- const bool trustEstablished);
+ const bool trustEstablished,
+ const uint64_t networkId);
/**
+ * Check whether we have an active path to this peer via the given address
+ *
* @param now Current time
* @param addr Remote address
* @return True if we have an active path to this destination
*/
- inline bool hasActivePathTo(uint64_t now,const InetAddress &addr) const
+ inline bool hasActivePathTo(int64_t now,const InetAddress &addr) const
{
Mutex::Lock _l(_paths_m);
- return ( ((addr.ss_family == AF_INET)&&(_v4Path.p)&&(_v4Path.p->address() == addr)&&(_v4Path.p->alive(now))) || ((addr.ss_family == AF_INET6)&&(_v6Path.p)&&(_v6Path.p->address() == addr)&&(_v6Path.p->alive(now))) );
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ if (((now - _paths[i].lr) < ZT_PEER_PATH_EXPIRATION)&&(_paths[i].p->address() == addr))
+ return true;
+ } else break;
+ }
+ return false;
}
/**
@@ -124,19 +143,27 @@ public:
* @param force If true, send even if path is not alive
* @return True if we actually sent something
*/
- bool sendDirect(void *tPtr,const void *data,unsigned int len,uint64_t now,bool force);
+ inline bool sendDirect(void *tPtr,const void *data,unsigned int len,int64_t now,bool force)
+ {
+ SharedPtr<Path> bp(getBestPath(now,force));
+ if (bp)
+ return bp->send(RR,tPtr,data,len,now);
+ return false;
+ }
/**
* Get the best current direct path
*
- * This does not check Path::alive(), but does return the most recently
- * active path and does check expiration (which is a longer timeout).
- *
* @param now Current time
* @param includeExpired If true, include even expired paths
* @return Best current path or NULL if none
*/
- SharedPtr<Path> getBestPath(uint64_t now,bool includeExpired);
+ SharedPtr<Path> getBestPath(int64_t now,bool includeExpired) const;
+
+ /**
+ * Send VERB_RENDEZVOUS to this and another peer via the best common IP scope and path
+ */
+ void introduce(void *const tPtr,const int64_t now,const SharedPtr<Peer> &other) const;
/**
* Send a HELLO to this peer at a specified physical address
@@ -144,12 +171,11 @@ public:
* No statistics or sent times are updated here.
*
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
- * @param localAddr Local address
+ * @param localSocket Local source socket
* @param atAddress Destination address
* @param now Current time
- * @param counter Outgoing packet counter
*/
- void sendHELLO(void *tPtr,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int counter);
+ void sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now);
/**
* Send ECHO (or HELLO for older peers) to this peer at the given address
@@ -157,13 +183,12 @@ public:
* No statistics or sent times are updated here.
*
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
- * @param localAddr Local address
+ * @param localSocket Local source socket
* @param atAddress Destination address
* @param now Current time
* @param sendFullHello If true, always send a full HELLO instead of just an ECHO
- * @param counter Outgoing packet counter
*/
- void attemptToContactAt(void *tPtr,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,bool sendFullHello,unsigned int counter);
+ void attemptToContactAt(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now,bool sendFullHello);
/**
* Try a memorized or statically defined path if any are known
@@ -173,106 +198,85 @@ public:
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param now Current time
*/
- void tryMemorizedPath(void *tPtr,uint64_t now);
+ void tryMemorizedPath(void *tPtr,int64_t now);
/**
* Send pings or keepalives depending on configured timeouts
*
+ * This also cleans up some internal data structures. It's called periodically from Node.
+ *
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param now Current time
* @param inetAddressFamily Keep this address family alive, or -1 for any
- * @return True if we have at least one direct path of the given family (or any if family is -1)
+ * @return 0 if nothing sent or bit mask: bit 0x1 if IPv4 sent, bit 0x2 if IPv6 sent (0x3 means both sent)
*/
- bool doPingAndKeepalive(void *tPtr,uint64_t now,int inetAddressFamily);
+ unsigned int doPingAndKeepalive(void *tPtr,int64_t now);
/**
- * Reset paths within a given IP scope and address family
- *
- * Resetting a path involves sending an ECHO to it and then deactivating
- * it until or unless it responds.
+ * Process a cluster redirect sent by this peer
*
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
- * @param scope IP scope
- * @param inetAddressFamily Family e.g. AF_INET
+ * @param originatingPath Path from which redirect originated
+ * @param remoteAddress Remote address
* @param now Current time
*/
- inline void resetWithinScope(void *tPtr,InetAddress::IpScope scope,int inetAddressFamily,uint64_t now)
- {
- Mutex::Lock _l(_paths_m);
- if ((inetAddressFamily == AF_INET)&&(_v4Path.lr)&&(_v4Path.p->address().ipScope() == scope)) {
- attemptToContactAt(tPtr,_v4Path.p->localAddress(),_v4Path.p->address(),now,false,_v4Path.p->nextOutgoingCounter());
- _v4Path.p->sent(now);
- _v4Path.lr = 0; // path will not be used unless it speaks again
- } else if ((inetAddressFamily == AF_INET6)&&(_v6Path.lr)&&(_v6Path.p->address().ipScope() == scope)) {
- attemptToContactAt(tPtr,_v6Path.p->localAddress(),_v6Path.p->address(),now,false,_v6Path.p->nextOutgoingCounter());
- _v6Path.p->sent(now);
- _v6Path.lr = 0; // path will not be used unless it speaks again
- }
- }
+ void clusterRedirect(void *tPtr,const SharedPtr<Path> &originatingPath,const InetAddress &remoteAddress,const int64_t now);
/**
- * Indicate that the given address was provided by a cluster as a preferred destination
+ * Reset paths within a given IP scope and address family
*
- * @param addr Address cluster prefers that we use
- */
- inline void setClusterPreferred(const InetAddress &addr)
- {
- if (addr.ss_family == AF_INET)
- _v4ClusterPreferred = addr;
- else if (addr.ss_family == AF_INET6)
- _v6ClusterPreferred = addr;
- }
-
- /**
- * Fill parameters with V4 and V6 addresses if known and alive
+ * Resetting a path involves sending an ECHO to it and then deactivating
+ * it until or unless it responds. This is done when we detect a change
+ * to our external IP or another system change that might invalidate
+ * many or all current paths.
*
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param scope IP scope
+ * @param inetAddressFamily Family e.g. AF_INET
* @param now Current time
- * @param v4 Result parameter to receive active IPv4 address, if any
- * @param v6 Result parameter to receive active IPv6 address, if any
*/
- inline void getRendezvousAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const
- {
- Mutex::Lock _l(_paths_m);
- if (((now - _v4Path.lr) < ZT_PEER_PATH_EXPIRATION)&&(_v4Path.p->alive(now)))
- v4 = _v4Path.p->address();
- if (((now - _v6Path.lr) < ZT_PEER_PATH_EXPIRATION)&&(_v6Path.p->alive(now)))
- v6 = _v6Path.p->address();
- }
+ void resetWithinScope(void *tPtr,InetAddress::IpScope scope,int inetAddressFamily,int64_t now);
/**
* @param now Current time
* @return All known paths to this peer
*/
- inline std::vector< SharedPtr<Path> > paths(const uint64_t now) const
+ inline std::vector< SharedPtr<Path> > paths(const int64_t now) const
{
std::vector< SharedPtr<Path> > pp;
Mutex::Lock _l(_paths_m);
- if (((now - _v4Path.lr) < ZT_PEER_PATH_EXPIRATION)&&(_v4Path.p->alive(now)))
- pp.push_back(_v4Path.p);
- if (((now - _v6Path.lr) < ZT_PEER_PATH_EXPIRATION)&&(_v6Path.p->alive(now)))
- pp.push_back(_v6Path.p);
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (!_paths[i].p) break;
+ pp.push_back(_paths[i].p);
+ }
return pp;
}
/**
* @return Time of last receive of anything, whether direct or relayed
*/
- inline uint64_t lastReceive() const { return _lastReceive; }
+ inline int64_t lastReceive() const { return _lastReceive; }
/**
* @return True if we've heard from this peer in less than ZT_PEER_ACTIVITY_TIMEOUT
*/
- inline bool isAlive(const uint64_t now) const { return ((now - _lastReceive) < ZT_PEER_ACTIVITY_TIMEOUT); }
+ inline bool isAlive(const int64_t now) const { return ((now - _lastReceive) < ZT_PEER_ACTIVITY_TIMEOUT); }
/**
* @return True if this peer has sent us real network traffic recently
*/
- inline uint64_t isActive(uint64_t now) const { return ((now - _lastNontrivialReceive) < ZT_PEER_ACTIVITY_TIMEOUT); }
+ inline int64_t isActive(int64_t now) const { return ((now - _lastNontrivialReceive) < ZT_PEER_ACTIVITY_TIMEOUT); }
/**
- * @return Latency in milliseconds or 0 if unknown
+ * @return Latency in milliseconds of best path or 0xffff if unknown / no paths
*/
- inline unsigned int latency() const { return _latency; }
+ inline unsigned int latency(const int64_t now) const
+ {
+ SharedPtr<Path> bp(getBestPath(now,false));
+ if (bp)
+ return bp->latency();
+ return 0xffff;
+ }
/**
* This computes a quality score for relays and root servers
@@ -285,43 +289,18 @@ public:
*
* @return Relay quality score computed from latency and other factors, lower is better
*/
- inline unsigned int relayQuality(const uint64_t now) const
+ inline unsigned int relayQuality(const int64_t now) const
{
const uint64_t tsr = now - _lastReceive;
if (tsr >= ZT_PEER_ACTIVITY_TIMEOUT)
return (~(unsigned int)0);
- unsigned int l = _latency;
+ unsigned int l = latency(now);
if (!l)
l = 0xffff;
return (l * (((unsigned int)tsr / (ZT_PEER_PING_PERIOD + 1000)) + 1));
}
/**
- * Update latency with a new direct measurment
- *
- * @param l Direct latency measurment in ms
- */
- inline void addDirectLatencyMeasurment(unsigned int l)
- {
- unsigned int ol = _latency;
- if ((ol > 0)&&(ol < 10000))
- _latency = (ol + std::min(l,(unsigned int)65535)) / 2;
- else _latency = std::min(l,(unsigned int)65535);
- }
-
-#ifdef ZT_ENABLE_CLUSTER
- /**
- * @param now Current time
- * @return True if this peer has at least one active direct path that is not cluster-suboptimal
- */
- inline bool hasLocalClusterOptimalPath(uint64_t now) const
- {
- Mutex::Lock _l(_paths_m);
- return ( ((_v4Path.p)&&(_v4Path.p->alive(now))&&(!_v4Path.localClusterSuboptimal)) || ((_v6Path.p)&&(_v6Path.p->alive(now))&&(!_v6Path.localClusterSuboptimal)) );
- }
-#endif
-
- /**
* @return 256-bit secret symmetric encryption key
*/
inline const unsigned char *key() const { return _key; }
@@ -352,12 +331,12 @@ public:
/**
* @return True if peer has received a trust established packet (e.g. common network membership) in the past ZT_TRUST_EXPIRATION ms
*/
- inline bool trustEstablished(const uint64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); }
+ inline bool trustEstablished(const int64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); }
/**
* Rate limit gate for VERB_PUSH_DIRECT_PATHS
*/
- inline bool rateGatePushDirectPaths(const uint64_t now)
+ inline bool rateGatePushDirectPaths(const int64_t now)
{
if ((now - _lastDirectPathPushReceive) <= ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME)
++_directPathPushCutoffCount;
@@ -369,7 +348,7 @@ public:
/**
* Rate limit gate for VERB_NETWORK_CREDENTIALS
*/
- inline bool rateGateCredentialsReceived(const uint64_t now)
+ inline bool rateGateCredentialsReceived(const int64_t now)
{
if ((now - _lastCredentialsReceived) <= ZT_PEER_CREDENTIALS_CUTOFF_TIME)
++_credentialsCutoffCount;
@@ -381,7 +360,7 @@ public:
/**
* Rate limit gate for sending of ERROR_NEED_MEMBERSHIP_CERTIFICATE
*/
- inline bool rateGateRequestCredentials(const uint64_t now)
+ inline bool rateGateRequestCredentials(const int64_t now)
{
if ((now - _lastCredentialRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) {
_lastCredentialRequestSent = now;
@@ -393,7 +372,7 @@ public:
/**
* Rate limit gate for inbound WHOIS requests
*/
- inline bool rateGateInboundWhoisRequest(const uint64_t now)
+ inline bool rateGateInboundWhoisRequest(const int64_t now)
{
if ((now - _lastWhoisRequestReceived) >= ZT_PEER_WHOIS_RATE_LIMIT) {
_lastWhoisRequestReceived = now;
@@ -405,7 +384,7 @@ public:
/**
* Rate limit gate for inbound ECHO requests
*/
- inline bool rateGateEchoRequest(const uint64_t now)
+ inline bool rateGateEchoRequest(const int64_t now)
{
if ((now - _lastEchoRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
_lastEchoRequestReceived = now;
@@ -417,7 +396,7 @@ public:
/**
* Rate gate incoming requests for network COM
*/
- inline bool rateGateIncomingComRequest(const uint64_t now)
+ inline bool rateGateIncomingComRequest(const int64_t now)
{
if ((now - _lastComRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
_lastComRequestReceived = now;
@@ -429,7 +408,7 @@ public:
/**
* Rate gate outgoing requests for network COM
*/
- inline bool rateGateOutgoingComRequest(const uint64_t now)
+ inline bool rateGateOutgoingComRequest(const int64_t now)
{
if ((now - _lastComRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) {
_lastComRequestSent = now;
@@ -438,53 +417,115 @@ public:
return false;
}
+ /**
+ * Serialize a peer for storage in local cache
+ *
+ * This does not serialize everything, just non-ephemeral information.
+ */
+ template<unsigned int C>
+ inline void serializeForCache(Buffer<C> &b) const
+ {
+ b.append((uint8_t)1);
+
+ _id.serialize(b);
+
+ b.append((uint16_t)_vProto);
+ b.append((uint16_t)_vMajor);
+ b.append((uint16_t)_vMinor);
+ b.append((uint16_t)_vRevision);
+
+ {
+ Mutex::Lock _l(_paths_m);
+ unsigned int pc = 0;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p)
+ ++pc;
+ else break;
+ }
+ b.append((uint16_t)pc);
+ for(unsigned int i=0;i<pc;++i)
+ _paths[i].p->address().serialize(b);
+ }
+ }
+
+ template<unsigned int C>
+ inline static SharedPtr<Peer> deserializeFromCache(int64_t now,void *tPtr,Buffer<C> &b,const RuntimeEnvironment *renv)
+ {
+ try {
+ unsigned int ptr = 0;
+ if (b[ptr++] != 1)
+ return SharedPtr<Peer>();
+
+ Identity id;
+ ptr += id.deserialize(b,ptr);
+ if (!id)
+ return SharedPtr<Peer>();
+
+ SharedPtr<Peer> p(new Peer(renv,renv->identity,id));
+
+ p->_vProto = b.template at<uint16_t>(ptr); ptr += 2;
+ p->_vMajor = b.template at<uint16_t>(ptr); ptr += 2;
+ p->_vMinor = b.template at<uint16_t>(ptr); ptr += 2;
+ p->_vRevision = b.template at<uint16_t>(ptr); ptr += 2;
+
+ // When we deserialize from the cache we don't actually restore paths. We
+ // just try them and then re-learn them if they happen to still be up.
+ // Paths are fairly ephemeral in the real world in most cases.
+ const unsigned int tryPathCount = b.template at<uint16_t>(ptr); ptr += 2;
+ for(unsigned int i=0;i<tryPathCount;++i) {
+ InetAddress inaddr;
+ try {
+ ptr += inaddr.deserialize(b,ptr);
+ if (inaddr)
+ p->attemptToContactAt(tPtr,-1,inaddr,now,true);
+ } catch ( ... ) {
+ break;
+ }
+ }
+
+ return p;
+ } catch ( ... ) {
+ return SharedPtr<Peer>();
+ }
+ }
+
private:
struct _PeerPath
{
-#ifdef ZT_ENABLE_CLUSTER
- _PeerPath() : lr(0),p(),localClusterSuboptimal(false) {}
-#else
- _PeerPath() : lr(0),p() {}
-#endif
- uint64_t lr; // time of last valid ZeroTier packet
+ _PeerPath() : lr(0),p(),priority(1) {}
+ int64_t lr; // time of last valid ZeroTier packet
SharedPtr<Path> p;
-#ifdef ZT_ENABLE_CLUSTER
- bool localClusterSuboptimal; // true if our cluster has determined that we should not be serving this peer
-#endif
+ long priority; // >= 1, higher is better
};
uint8_t _key[ZT_PEER_SECRET_KEY_LENGTH];
const RuntimeEnvironment *RR;
- uint64_t _lastReceive; // direct or indirect
- uint64_t _lastNontrivialReceive; // frames, things like netconf, etc.
- uint64_t _lastTriedMemorizedPath;
- uint64_t _lastDirectPathPushSent;
- uint64_t _lastDirectPathPushReceive;
- uint64_t _lastCredentialRequestSent;
- uint64_t _lastWhoisRequestReceived;
- uint64_t _lastEchoRequestReceived;
- uint64_t _lastComRequestReceived;
- uint64_t _lastComRequestSent;
- uint64_t _lastCredentialsReceived;
- uint64_t _lastTrustEstablishedPacketReceived;
+ int64_t _lastReceive; // direct or indirect
+ int64_t _lastNontrivialReceive; // frames, things like netconf, etc.
+ int64_t _lastTriedMemorizedPath;
+ int64_t _lastDirectPathPushSent;
+ int64_t _lastDirectPathPushReceive;
+ int64_t _lastCredentialRequestSent;
+ int64_t _lastWhoisRequestReceived;
+ int64_t _lastEchoRequestReceived;
+ int64_t _lastComRequestReceived;
+ int64_t _lastComRequestSent;
+ int64_t _lastCredentialsReceived;
+ int64_t _lastTrustEstablishedPacketReceived;
+ int64_t _lastSentFullHello;
uint16_t _vProto;
uint16_t _vMajor;
uint16_t _vMinor;
uint16_t _vRevision;
- InetAddress _v4ClusterPreferred;
- InetAddress _v6ClusterPreferred;
-
- _PeerPath _v4Path; // IPv4 direct path
- _PeerPath _v6Path; // IPv6 direct path
+ _PeerPath _paths[ZT_MAX_PEER_NETWORK_PATHS];
Mutex _paths_m;
Identity _id;
- unsigned int _latency;
unsigned int _directPathPushCutoffCount;
unsigned int _credentialsCutoffCount;
diff --git a/node/Poly1305.cpp b/node/Poly1305.cpp
index 13d4712d..eceb57b3 100644
--- a/node/Poly1305.cpp
+++ b/node/Poly1305.cpp
@@ -121,7 +121,6 @@ static inline int crypto_onetimeauth(unsigned char *out,const unsigned char *in,
}
void Poly1305::compute(void *auth,const void *data,unsigned int len,const void *key)
- throw()
{
crypto_onetimeauth((unsigned char *)auth,(const unsigned char *)data,len,(const unsigned char *)key);
}
@@ -140,7 +139,6 @@ typedef struct poly1305_context {
//////////////////////////////////////////////////////////////////////////////
// 128-bit implementation for MSC and GCC from Poly1305-donna
-
#if defined(_MSC_VER)
#include <intrin.h>
@@ -624,7 +622,6 @@ poly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes) {
} // anonymous namespace
void Poly1305::compute(void *auth,const void *data,unsigned int len,const void *key)
- throw()
{
poly1305_context ctx;
poly1305_init(&ctx,reinterpret_cast<const unsigned char *>(key));
diff --git a/node/Poly1305.hpp b/node/Poly1305.hpp
index 62d57546..adcc2410 100644
--- a/node/Poly1305.hpp
+++ b/node/Poly1305.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_POLY1305_HPP
@@ -46,8 +54,7 @@ public:
* @param len Length of data to authenticate in bytes
* @param key 32-byte one-time use key to authenticate data (must not be reused)
*/
- static void compute(void *auth,const void *data,unsigned int len,const void *key)
- throw();
+ static void compute(void *auth,const void *data,unsigned int len,const void *key);
};
} // namespace ZeroTier
diff --git a/node/Revocation.cpp b/node/Revocation.cpp
index bab5653c..78098f8c 100644
--- a/node/Revocation.cpp
+++ b/node/Revocation.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include "Revocation.hpp"
@@ -22,6 +30,7 @@
#include "Topology.hpp"
#include "Switch.hpp"
#include "Network.hpp"
+#include "Node.hpp"
namespace ZeroTier {
@@ -31,7 +40,7 @@ int Revocation::verify(const RuntimeEnvironment *RR,void *tPtr) const
return -1;
const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
if (!id) {
- RR->sw->requestWhois(tPtr,_signedBy);
+ RR->sw->requestWhois(tPtr,RR->node->now(),_signedBy);
return 1;
}
try {
diff --git a/node/Revocation.hpp b/node/Revocation.hpp
index e5e013bd..eaf01915 100644
--- a/node/Revocation.hpp
+++ b/node/Revocation.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_REVOCATION_HPP
@@ -77,7 +85,7 @@ public:
inline uint32_t id() const { return _id; }
inline uint32_t credentialId() const { return _credentialId; }
inline uint64_t networkId() const { return _networkId; }
- inline uint64_t threshold() const { return _threshold; }
+ inline int64_t threshold() const { return _threshold; }
inline const Address &target() const { return _target; }
inline const Address &signer() const { return _signedBy; }
inline Credential::Type type() const { return _type; }
@@ -158,16 +166,16 @@ public:
if (b[p++] == 1) {
if (b.template at<uint16_t>(p) == ZT_C25519_SIGNATURE_LEN) {
p += 2;
- memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
+ ZT_FAST_MEMCPY(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
p += ZT_C25519_SIGNATURE_LEN;
- } else throw std::runtime_error("invalid signature");
+ } else throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
} else {
p += 2 + b.template at<uint16_t>(p);
}
p += 2 + b.template at<uint16_t>(p);
if (p > b.size())
- throw std::runtime_error("extended field overflow");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
return (p - startAt);
}
@@ -176,7 +184,7 @@ private:
uint32_t _id;
uint32_t _credentialId;
uint64_t _networkId;
- uint64_t _threshold;
+ int64_t _threshold;
uint64_t _flags;
Address _target;
Address _signedBy;
diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp
index 7ba1c989..46350b4a 100644
--- a/node/RuntimeEnvironment.hpp
+++ b/node/RuntimeEnvironment.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,16 +14,24 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_RUNTIMEENVIRONMENT_HPP
#define ZT_RUNTIMEENVIRONMENT_HPP
-#include <string>
+#include <string.h>
#include "Constants.hpp"
+#include "Utils.hpp"
#include "Identity.hpp"
-#include "Mutex.hpp"
namespace ZeroTier {
@@ -34,7 +42,7 @@ class Node;
class Multicaster;
class NetworkController;
class SelfAwareness;
-class Cluster;
+class Trace;
/**
* Holds global state for an instance of ZeroTier::Node
@@ -44,44 +52,47 @@ class RuntimeEnvironment
public:
RuntimeEnvironment(Node *n) :
node(n)
- ,identity()
,localNetworkController((NetworkController *)0)
+ ,rtmem((void *)0)
,sw((Switch *)0)
,mc((Multicaster *)0)
,topology((Topology *)0)
,sa((SelfAwareness *)0)
-#ifdef ZT_ENABLE_CLUSTER
- ,cluster((Cluster *)0)
-#endif
{
+ publicIdentityStr[0] = (char)0;
+ secretIdentityStr[0] = (char)0;
+ }
+
+ ~RuntimeEnvironment()
+ {
+ Utils::burn(secretIdentityStr,sizeof(secretIdentityStr));
}
// Node instance that owns this RuntimeEnvironment
Node *const node;
- // This node's identity
- Identity identity;
- std::string publicIdentityStr;
- std::string secretIdentityStr;
-
// This is set externally to an instance of this base class
NetworkController *localNetworkController;
- /*
- * Order matters a bit here. These are constructed in this order
+ // Memory actually occupied by Trace, Switch, etc.
+ void *rtmem;
+
+ /* Order matters a bit here. These are constructed in this order
* and then deleted in the opposite order on Node exit. The order ensures
* that things that are needed are there before they're needed.
*
- * These are constant and never null after startup unless indicated.
- */
+ * These are constant and never null after startup unless indicated. */
+ Trace *t;
Switch *sw;
Multicaster *mc;
Topology *topology;
SelfAwareness *sa;
-#ifdef ZT_ENABLE_CLUSTER
- Cluster *cluster;
-#endif
+
+ // This node's identity and string representations thereof
+ Identity identity;
+ char publicIdentityStr[ZT_IDENTITY_STRING_BUFFER_LENGTH];
+ char secretIdentityStr[ZT_IDENTITY_STRING_BUFFER_LENGTH];
};
} // namespace ZeroTier
diff --git a/node/SHA512.cpp b/node/SHA512.cpp
index 76737d37..d3c938af 100644
--- a/node/SHA512.cpp
+++ b/node/SHA512.cpp
@@ -1,20 +1,11 @@
+// Code taken from NaCl by D. J. Bernstein and others
+// Public domain
+
/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
+20080913
+D. J. Bernstein
+Public domain.
+*/
#include <stdint.h>
#include <stdlib.h>
@@ -23,19 +14,37 @@
#include "SHA512.hpp"
#include "Utils.hpp"
+#ifdef __APPLE__
+#include <CommonCrypto/CommonDigest.h>
+#define ZT_HAVE_NATIVE_SHA512
namespace ZeroTier {
+void SHA512::hash(void *digest,const void *data,unsigned int len)
+{
+ CC_SHA512_CTX ctx;
+ CC_SHA512_Init(&ctx);
+ CC_SHA512_Update(&ctx,data,len);
+ CC_SHA512_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
+}
+}
+#endif
-//////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////
+#ifdef ZT_USE_LIBCRYPTO
+#include <openssl/sha.h>
+#define ZT_HAVE_NATIVE_SHA512
+namespace ZeroTier {
+void SHA512::hash(void *digest,const void *data,unsigned int len)
+{
+ SHA512_CTX ctx;
+ SHA512_Init(&ctx);
+ SHA512_Update(&ctx,data,len);
+ SHA512_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
+}
+}
+#endif
-// Code taken from NaCl by D. J. Bernstein and others
-// Public domain
+#ifndef ZT_HAVE_NATIVE_SHA512
-/*
-20080913
-D. J. Bernstein
-Public domain.
-*/
+namespace ZeroTier {
#define uint64 uint64_t
@@ -43,28 +52,28 @@ Public domain.
static uint64 load_bigendian(const unsigned char *x)
{
- return
- (uint64) (x[7]) \
- | (((uint64) (x[6])) << 8) \
- | (((uint64) (x[5])) << 16) \
- | (((uint64) (x[4])) << 24) \
- | (((uint64) (x[3])) << 32) \
- | (((uint64) (x[2])) << 40) \
- | (((uint64) (x[1])) << 48) \
- | (((uint64) (x[0])) << 56)
- ;
+ return
+ (uint64) (x[7]) \
+ | (((uint64) (x[6])) << 8) \
+ | (((uint64) (x[5])) << 16) \
+ | (((uint64) (x[4])) << 24) \
+ | (((uint64) (x[3])) << 32) \
+ | (((uint64) (x[2])) << 40) \
+ | (((uint64) (x[1])) << 48) \
+ | (((uint64) (x[0])) << 56)
+ ;
}
static void store_bigendian(unsigned char *x,uint64 u)
{
- x[7] = u; u >>= 8;
- x[6] = u; u >>= 8;
- x[5] = u; u >>= 8;
- x[4] = u; u >>= 8;
- x[3] = u; u >>= 8;
- x[2] = u; u >>= 8;
- x[1] = u; u >>= 8;
- x[0] = u;
+ x[7] = u; u >>= 8;
+ x[6] = u; u >>= 8;
+ x[5] = u; u >>= 8;
+ x[4] = u; u >>= 8;
+ x[3] = u; u >>= 8;
+ x[2] = u; u >>= 8;
+ x[1] = u; u >>= 8;
+ x[0] = u;
}
#else // !ZT_NO_TYPE_PUNNING
@@ -87,266 +96,272 @@ static void store_bigendian(unsigned char *x,uint64 u)
#define M(w0,w14,w9,w1) w0 = sigma1(w14) + w9 + sigma0(w1) + w0;
#define EXPAND \
- M(w0 ,w14,w9 ,w1 ) \
- M(w1 ,w15,w10,w2 ) \
- M(w2 ,w0 ,w11,w3 ) \
- M(w3 ,w1 ,w12,w4 ) \
- M(w4 ,w2 ,w13,w5 ) \
- M(w5 ,w3 ,w14,w6 ) \
- M(w6 ,w4 ,w15,w7 ) \
- M(w7 ,w5 ,w0 ,w8 ) \
- M(w8 ,w6 ,w1 ,w9 ) \
- M(w9 ,w7 ,w2 ,w10) \
- M(w10,w8 ,w3 ,w11) \
- M(w11,w9 ,w4 ,w12) \
- M(w12,w10,w5 ,w13) \
- M(w13,w11,w6 ,w14) \
- M(w14,w12,w7 ,w15) \
- M(w15,w13,w8 ,w0 )
+ M(w0 ,w14,w9 ,w1 ) \
+ M(w1 ,w15,w10,w2 ) \
+ M(w2 ,w0 ,w11,w3 ) \
+ M(w3 ,w1 ,w12,w4 ) \
+ M(w4 ,w2 ,w13,w5 ) \
+ M(w5 ,w3 ,w14,w6 ) \
+ M(w6 ,w4 ,w15,w7 ) \
+ M(w7 ,w5 ,w0 ,w8 ) \
+ M(w8 ,w6 ,w1 ,w9 ) \
+ M(w9 ,w7 ,w2 ,w10) \
+ M(w10,w8 ,w3 ,w11) \
+ M(w11,w9 ,w4 ,w12) \
+ M(w12,w10,w5 ,w13) \
+ M(w13,w11,w6 ,w14) \
+ M(w14,w12,w7 ,w15) \
+ M(w15,w13,w8 ,w0 )
#define F(w,k) \
- T1 = h + Sigma1(e) + Ch(e,f,g) + k + w; \
- T2 = Sigma0(a) + Maj(a,b,c); \
- h = g; \
- g = f; \
- f = e; \
- e = d + T1; \
- d = c; \
- c = b; \
- b = a; \
- a = T1 + T2;
+ T1 = h + Sigma1(e) + Ch(e,f,g) + k + w; \
+ T2 = Sigma0(a) + Maj(a,b,c); \
+ h = g; \
+ g = f; \
+ f = e; \
+ e = d + T1; \
+ d = c; \
+ c = b; \
+ b = a; \
+ a = T1 + T2;
static inline int crypto_hashblocks(unsigned char *statebytes,const unsigned char *in,unsigned long long inlen)
{
- uint64 state[8];
- uint64 a;
- uint64 b;
- uint64 c;
- uint64 d;
- uint64 e;
- uint64 f;
- uint64 g;
- uint64 h;
- uint64 T1;
- uint64 T2;
-
- a = load_bigendian(statebytes + 0); state[0] = a;
- b = load_bigendian(statebytes + 8); state[1] = b;
- c = load_bigendian(statebytes + 16); state[2] = c;
- d = load_bigendian(statebytes + 24); state[3] = d;
- e = load_bigendian(statebytes + 32); state[4] = e;
- f = load_bigendian(statebytes + 40); state[5] = f;
- g = load_bigendian(statebytes + 48); state[6] = g;
- h = load_bigendian(statebytes + 56); state[7] = h;
-
- while (inlen >= 128) {
- uint64 w0 = load_bigendian(in + 0);
- uint64 w1 = load_bigendian(in + 8);
- uint64 w2 = load_bigendian(in + 16);
- uint64 w3 = load_bigendian(in + 24);
- uint64 w4 = load_bigendian(in + 32);
- uint64 w5 = load_bigendian(in + 40);
- uint64 w6 = load_bigendian(in + 48);
- uint64 w7 = load_bigendian(in + 56);
- uint64 w8 = load_bigendian(in + 64);
- uint64 w9 = load_bigendian(in + 72);
- uint64 w10 = load_bigendian(in + 80);
- uint64 w11 = load_bigendian(in + 88);
- uint64 w12 = load_bigendian(in + 96);
- uint64 w13 = load_bigendian(in + 104);
- uint64 w14 = load_bigendian(in + 112);
- uint64 w15 = load_bigendian(in + 120);
-
- F(w0 ,0x428a2f98d728ae22ULL)
- F(w1 ,0x7137449123ef65cdULL)
- F(w2 ,0xb5c0fbcfec4d3b2fULL)
- F(w3 ,0xe9b5dba58189dbbcULL)
- F(w4 ,0x3956c25bf348b538ULL)
- F(w5 ,0x59f111f1b605d019ULL)
- F(w6 ,0x923f82a4af194f9bULL)
- F(w7 ,0xab1c5ed5da6d8118ULL)
- F(w8 ,0xd807aa98a3030242ULL)
- F(w9 ,0x12835b0145706fbeULL)
- F(w10,0x243185be4ee4b28cULL)
- F(w11,0x550c7dc3d5ffb4e2ULL)
- F(w12,0x72be5d74f27b896fULL)
- F(w13,0x80deb1fe3b1696b1ULL)
- F(w14,0x9bdc06a725c71235ULL)
- F(w15,0xc19bf174cf692694ULL)
-
- EXPAND
-
- F(w0 ,0xe49b69c19ef14ad2ULL)
- F(w1 ,0xefbe4786384f25e3ULL)
- F(w2 ,0x0fc19dc68b8cd5b5ULL)
- F(w3 ,0x240ca1cc77ac9c65ULL)
- F(w4 ,0x2de92c6f592b0275ULL)
- F(w5 ,0x4a7484aa6ea6e483ULL)
- F(w6 ,0x5cb0a9dcbd41fbd4ULL)
- F(w7 ,0x76f988da831153b5ULL)
- F(w8 ,0x983e5152ee66dfabULL)
- F(w9 ,0xa831c66d2db43210ULL)
- F(w10,0xb00327c898fb213fULL)
- F(w11,0xbf597fc7beef0ee4ULL)
- F(w12,0xc6e00bf33da88fc2ULL)
- F(w13,0xd5a79147930aa725ULL)
- F(w14,0x06ca6351e003826fULL)
- F(w15,0x142929670a0e6e70ULL)
-
- EXPAND
-
- F(w0 ,0x27b70a8546d22ffcULL)
- F(w1 ,0x2e1b21385c26c926ULL)
- F(w2 ,0x4d2c6dfc5ac42aedULL)
- F(w3 ,0x53380d139d95b3dfULL)
- F(w4 ,0x650a73548baf63deULL)
- F(w5 ,0x766a0abb3c77b2a8ULL)
- F(w6 ,0x81c2c92e47edaee6ULL)
- F(w7 ,0x92722c851482353bULL)
- F(w8 ,0xa2bfe8a14cf10364ULL)
- F(w9 ,0xa81a664bbc423001ULL)
- F(w10,0xc24b8b70d0f89791ULL)
- F(w11,0xc76c51a30654be30ULL)
- F(w12,0xd192e819d6ef5218ULL)
- F(w13,0xd69906245565a910ULL)
- F(w14,0xf40e35855771202aULL)
- F(w15,0x106aa07032bbd1b8ULL)
-
- EXPAND
-
- F(w0 ,0x19a4c116b8d2d0c8ULL)
- F(w1 ,0x1e376c085141ab53ULL)
- F(w2 ,0x2748774cdf8eeb99ULL)
- F(w3 ,0x34b0bcb5e19b48a8ULL)
- F(w4 ,0x391c0cb3c5c95a63ULL)
- F(w5 ,0x4ed8aa4ae3418acbULL)
- F(w6 ,0x5b9cca4f7763e373ULL)
- F(w7 ,0x682e6ff3d6b2b8a3ULL)
- F(w8 ,0x748f82ee5defb2fcULL)
- F(w9 ,0x78a5636f43172f60ULL)
- F(w10,0x84c87814a1f0ab72ULL)
- F(w11,0x8cc702081a6439ecULL)
- F(w12,0x90befffa23631e28ULL)
- F(w13,0xa4506cebde82bde9ULL)
- F(w14,0xbef9a3f7b2c67915ULL)
- F(w15,0xc67178f2e372532bULL)
-
- EXPAND
-
- F(w0 ,0xca273eceea26619cULL)
- F(w1 ,0xd186b8c721c0c207ULL)
- F(w2 ,0xeada7dd6cde0eb1eULL)
- F(w3 ,0xf57d4f7fee6ed178ULL)
- F(w4 ,0x06f067aa72176fbaULL)
- F(w5 ,0x0a637dc5a2c898a6ULL)
- F(w6 ,0x113f9804bef90daeULL)
- F(w7 ,0x1b710b35131c471bULL)
- F(w8 ,0x28db77f523047d84ULL)
- F(w9 ,0x32caab7b40c72493ULL)
- F(w10,0x3c9ebe0a15c9bebcULL)
- F(w11,0x431d67c49c100d4cULL)
- F(w12,0x4cc5d4becb3e42b6ULL)
- F(w13,0x597f299cfc657e2aULL)
- F(w14,0x5fcb6fab3ad6faecULL)
- F(w15,0x6c44198c4a475817ULL)
-
- a += state[0];
- b += state[1];
- c += state[2];
- d += state[3];
- e += state[4];
- f += state[5];
- g += state[6];
- h += state[7];
-
- state[0] = a;
- state[1] = b;
- state[2] = c;
- state[3] = d;
- state[4] = e;
- state[5] = f;
- state[6] = g;
- state[7] = h;
-
- in += 128;
- inlen -= 128;
- }
-
- store_bigendian(statebytes + 0,state[0]);
- store_bigendian(statebytes + 8,state[1]);
- store_bigendian(statebytes + 16,state[2]);
- store_bigendian(statebytes + 24,state[3]);
- store_bigendian(statebytes + 32,state[4]);
- store_bigendian(statebytes + 40,state[5]);
- store_bigendian(statebytes + 48,state[6]);
- store_bigendian(statebytes + 56,state[7]);
-
- return 0;
+ uint64 state[8];
+ uint64 a;
+ uint64 b;
+ uint64 c;
+ uint64 d;
+ uint64 e;
+ uint64 f;
+ uint64 g;
+ uint64 h;
+ uint64 T1;
+ uint64 T2;
+
+ a = load_bigendian(statebytes + 0); state[0] = a;
+ b = load_bigendian(statebytes + 8); state[1] = b;
+ c = load_bigendian(statebytes + 16); state[2] = c;
+ d = load_bigendian(statebytes + 24); state[3] = d;
+ e = load_bigendian(statebytes + 32); state[4] = e;
+ f = load_bigendian(statebytes + 40); state[5] = f;
+ g = load_bigendian(statebytes + 48); state[6] = g;
+ h = load_bigendian(statebytes + 56); state[7] = h;
+
+ while (inlen >= 128) {
+ uint64 w0 = load_bigendian(in + 0);
+ uint64 w1 = load_bigendian(in + 8);
+ uint64 w2 = load_bigendian(in + 16);
+ uint64 w3 = load_bigendian(in + 24);
+ uint64 w4 = load_bigendian(in + 32);
+ uint64 w5 = load_bigendian(in + 40);
+ uint64 w6 = load_bigendian(in + 48);
+ uint64 w7 = load_bigendian(in + 56);
+ uint64 w8 = load_bigendian(in + 64);
+ uint64 w9 = load_bigendian(in + 72);
+ uint64 w10 = load_bigendian(in + 80);
+ uint64 w11 = load_bigendian(in + 88);
+ uint64 w12 = load_bigendian(in + 96);
+ uint64 w13 = load_bigendian(in + 104);
+ uint64 w14 = load_bigendian(in + 112);
+ uint64 w15 = load_bigendian(in + 120);
+
+ F(w0 ,0x428a2f98d728ae22ULL)
+ F(w1 ,0x7137449123ef65cdULL)
+ F(w2 ,0xb5c0fbcfec4d3b2fULL)
+ F(w3 ,0xe9b5dba58189dbbcULL)
+ F(w4 ,0x3956c25bf348b538ULL)
+ F(w5 ,0x59f111f1b605d019ULL)
+ F(w6 ,0x923f82a4af194f9bULL)
+ F(w7 ,0xab1c5ed5da6d8118ULL)
+ F(w8 ,0xd807aa98a3030242ULL)
+ F(w9 ,0x12835b0145706fbeULL)
+ F(w10,0x243185be4ee4b28cULL)
+ F(w11,0x550c7dc3d5ffb4e2ULL)
+ F(w12,0x72be5d74f27b896fULL)
+ F(w13,0x80deb1fe3b1696b1ULL)
+ F(w14,0x9bdc06a725c71235ULL)
+ F(w15,0xc19bf174cf692694ULL)
+
+ EXPAND
+
+ F(w0 ,0xe49b69c19ef14ad2ULL)
+ F(w1 ,0xefbe4786384f25e3ULL)
+ F(w2 ,0x0fc19dc68b8cd5b5ULL)
+ F(w3 ,0x240ca1cc77ac9c65ULL)
+ F(w4 ,0x2de92c6f592b0275ULL)
+ F(w5 ,0x4a7484aa6ea6e483ULL)
+ F(w6 ,0x5cb0a9dcbd41fbd4ULL)
+ F(w7 ,0x76f988da831153b5ULL)
+ F(w8 ,0x983e5152ee66dfabULL)
+ F(w9 ,0xa831c66d2db43210ULL)
+ F(w10,0xb00327c898fb213fULL)
+ F(w11,0xbf597fc7beef0ee4ULL)
+ F(w12,0xc6e00bf33da88fc2ULL)
+ F(w13,0xd5a79147930aa725ULL)
+ F(w14,0x06ca6351e003826fULL)
+ F(w15,0x142929670a0e6e70ULL)
+
+ EXPAND
+
+ F(w0 ,0x27b70a8546d22ffcULL)
+ F(w1 ,0x2e1b21385c26c926ULL)
+ F(w2 ,0x4d2c6dfc5ac42aedULL)
+ F(w3 ,0x53380d139d95b3dfULL)
+ F(w4 ,0x650a73548baf63deULL)
+ F(w5 ,0x766a0abb3c77b2a8ULL)
+ F(w6 ,0x81c2c92e47edaee6ULL)
+ F(w7 ,0x92722c851482353bULL)
+ F(w8 ,0xa2bfe8a14cf10364ULL)
+ F(w9 ,0xa81a664bbc423001ULL)
+ F(w10,0xc24b8b70d0f89791ULL)
+ F(w11,0xc76c51a30654be30ULL)
+ F(w12,0xd192e819d6ef5218ULL)
+ F(w13,0xd69906245565a910ULL)
+ F(w14,0xf40e35855771202aULL)
+ F(w15,0x106aa07032bbd1b8ULL)
+
+ EXPAND
+
+ F(w0 ,0x19a4c116b8d2d0c8ULL)
+ F(w1 ,0x1e376c085141ab53ULL)
+ F(w2 ,0x2748774cdf8eeb99ULL)
+ F(w3 ,0x34b0bcb5e19b48a8ULL)
+ F(w4 ,0x391c0cb3c5c95a63ULL)
+ F(w5 ,0x4ed8aa4ae3418acbULL)
+ F(w6 ,0x5b9cca4f7763e373ULL)
+ F(w7 ,0x682e6ff3d6b2b8a3ULL)
+ F(w8 ,0x748f82ee5defb2fcULL)
+ F(w9 ,0x78a5636f43172f60ULL)
+ F(w10,0x84c87814a1f0ab72ULL)
+ F(w11,0x8cc702081a6439ecULL)
+ F(w12,0x90befffa23631e28ULL)
+ F(w13,0xa4506cebde82bde9ULL)
+ F(w14,0xbef9a3f7b2c67915ULL)
+ F(w15,0xc67178f2e372532bULL)
+
+ EXPAND
+
+ F(w0 ,0xca273eceea26619cULL)
+ F(w1 ,0xd186b8c721c0c207ULL)
+ F(w2 ,0xeada7dd6cde0eb1eULL)
+ F(w3 ,0xf57d4f7fee6ed178ULL)
+ F(w4 ,0x06f067aa72176fbaULL)
+ F(w5 ,0x0a637dc5a2c898a6ULL)
+ F(w6 ,0x113f9804bef90daeULL)
+ F(w7 ,0x1b710b35131c471bULL)
+ F(w8 ,0x28db77f523047d84ULL)
+ F(w9 ,0x32caab7b40c72493ULL)
+ F(w10,0x3c9ebe0a15c9bebcULL)
+ F(w11,0x431d67c49c100d4cULL)
+ F(w12,0x4cc5d4becb3e42b6ULL)
+ F(w13,0x597f299cfc657e2aULL)
+ F(w14,0x5fcb6fab3ad6faecULL)
+ F(w15,0x6c44198c4a475817ULL)
+
+ a += state[0];
+ b += state[1];
+ c += state[2];
+ d += state[3];
+ e += state[4];
+ f += state[5];
+ g += state[6];
+ h += state[7];
+
+ state[0] = a;
+ state[1] = b;
+ state[2] = c;
+ state[3] = d;
+ state[4] = e;
+ state[5] = f;
+ state[6] = g;
+ state[7] = h;
+
+ in += 128;
+ inlen -= 128;
+ }
+
+ store_bigendian(statebytes + 0,state[0]);
+ store_bigendian(statebytes + 8,state[1]);
+ store_bigendian(statebytes + 16,state[2]);
+ store_bigendian(statebytes + 24,state[3]);
+ store_bigendian(statebytes + 32,state[4]);
+ store_bigendian(statebytes + 40,state[5]);
+ store_bigendian(statebytes + 48,state[6]);
+ store_bigendian(statebytes + 56,state[7]);
+
+ return 0;
}
#define blocks crypto_hashblocks
static const unsigned char iv[64] = {
- 0x6a,0x09,0xe6,0x67,0xf3,0xbc,0xc9,0x08,
- 0xbb,0x67,0xae,0x85,0x84,0xca,0xa7,0x3b,
- 0x3c,0x6e,0xf3,0x72,0xfe,0x94,0xf8,0x2b,
- 0xa5,0x4f,0xf5,0x3a,0x5f,0x1d,0x36,0xf1,
- 0x51,0x0e,0x52,0x7f,0xad,0xe6,0x82,0xd1,
- 0x9b,0x05,0x68,0x8c,0x2b,0x3e,0x6c,0x1f,
- 0x1f,0x83,0xd9,0xab,0xfb,0x41,0xbd,0x6b,
- 0x5b,0xe0,0xcd,0x19,0x13,0x7e,0x21,0x79
+ 0x6a,0x09,0xe6,0x67,0xf3,0xbc,0xc9,0x08,
+ 0xbb,0x67,0xae,0x85,0x84,0xca,0xa7,0x3b,
+ 0x3c,0x6e,0xf3,0x72,0xfe,0x94,0xf8,0x2b,
+ 0xa5,0x4f,0xf5,0x3a,0x5f,0x1d,0x36,0xf1,
+ 0x51,0x0e,0x52,0x7f,0xad,0xe6,0x82,0xd1,
+ 0x9b,0x05,0x68,0x8c,0x2b,0x3e,0x6c,0x1f,
+ 0x1f,0x83,0xd9,0xab,0xfb,0x41,0xbd,0x6b,
+ 0x5b,0xe0,0xcd,0x19,0x13,0x7e,0x21,0x79
};
-//////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////
-
void SHA512::hash(void *digest,const void *data,unsigned int len)
{
- unsigned char h[64];
- unsigned char padded[256];
- int i;
- uint64_t bytes = len;
-
- const unsigned char *in = (const unsigned char *)data;
- unsigned int inlen = len;
-
- for (i = 0;i < 64;++i) h[i] = iv[i];
-
- blocks(h,in,inlen);
- in += inlen;
- inlen &= 127;
- in -= inlen;
-
- for (i = 0;i < (int)inlen;++i) padded[i] = in[i];
- padded[inlen] = 0x80;
-
- if (inlen < 112) {
- for (i = inlen + 1;i < 119;++i) padded[i] = 0;
- padded[119] = (unsigned char)((bytes >> 61) & 0xff);
- padded[120] = (unsigned char)((bytes >> 53) & 0xff);
- padded[121] = (unsigned char)((bytes >> 45) & 0xff);
- padded[122] = (unsigned char)((bytes >> 37) & 0xff);
- padded[123] = (unsigned char)((bytes >> 29) & 0xff);
- padded[124] = (unsigned char)((bytes >> 21) & 0xff);
- padded[125] = (unsigned char)((bytes >> 13) & 0xff);
- padded[126] = (unsigned char)((bytes >> 5) & 0xff);
- padded[127] = (unsigned char)((bytes << 3) & 0xff);
- blocks(h,padded,128);
- } else {
- for (i = inlen + 1;i < 247;++i) padded[i] = 0;
- padded[247] = (unsigned char)((bytes >> 61) & 0xff);
- padded[248] = (unsigned char)((bytes >> 53) & 0xff);
- padded[249] = (unsigned char)((bytes >> 45) & 0xff);
- padded[250] = (unsigned char)((bytes >> 37) & 0xff);
- padded[251] = (unsigned char)((bytes >> 29) & 0xff);
- padded[252] = (unsigned char)((bytes >> 21) & 0xff);
- padded[253] = (unsigned char)((bytes >> 13) & 0xff);
- padded[254] = (unsigned char)((bytes >> 5) & 0xff);
- padded[255] = (unsigned char)((bytes << 3) & 0xff);
- blocks(h,padded,256);
- }
-
- for (i = 0;i < 64;++i) ((unsigned char *)digest)[i] = h[i];
+ unsigned char h[64];
+ unsigned char padded[256];
+ int i;
+ uint64_t bytes = len;
+
+ const unsigned char *in = (const unsigned char *)data;
+ unsigned int inlen = len;
+
+ for (i = 0;i < 64;++i) h[i] = iv[i];
+
+ blocks(h,in,inlen);
+ in += inlen;
+ inlen &= 127;
+ in -= inlen;
+
+ for (i = 0;i < (int)inlen;++i) padded[i] = in[i];
+ padded[inlen] = 0x80;
+
+ if (inlen < 112) {
+ for (i = inlen + 1;i < 119;++i) padded[i] = 0;
+ padded[119] = (unsigned char)((bytes >> 61) & 0xff);
+ padded[120] = (unsigned char)((bytes >> 53) & 0xff);
+ padded[121] = (unsigned char)((bytes >> 45) & 0xff);
+ padded[122] = (unsigned char)((bytes >> 37) & 0xff);
+ padded[123] = (unsigned char)((bytes >> 29) & 0xff);
+ padded[124] = (unsigned char)((bytes >> 21) & 0xff);
+ padded[125] = (unsigned char)((bytes >> 13) & 0xff);
+ padded[126] = (unsigned char)((bytes >> 5) & 0xff);
+ padded[127] = (unsigned char)((bytes << 3) & 0xff);
+ blocks(h,padded,128);
+ } else {
+ for (i = inlen + 1;i < 247;++i) padded[i] = 0;
+ padded[247] = (unsigned char)((bytes >> 61) & 0xff);
+ padded[248] = (unsigned char)((bytes >> 53) & 0xff);
+ padded[249] = (unsigned char)((bytes >> 45) & 0xff);
+ padded[250] = (unsigned char)((bytes >> 37) & 0xff);
+ padded[251] = (unsigned char)((bytes >> 29) & 0xff);
+ padded[252] = (unsigned char)((bytes >> 21) & 0xff);
+ padded[253] = (unsigned char)((bytes >> 13) & 0xff);
+ padded[254] = (unsigned char)((bytes >> 5) & 0xff);
+ padded[255] = (unsigned char)((bytes << 3) & 0xff);
+ blocks(h,padded,256);
+ }
+
+ for (i = 0;i < 64;++i) ((unsigned char *)digest)[i] = h[i];
}
} // namespace ZeroTier
+
+#endif // !ZT_HAVE_NATIVE_SHA512
+
+// Internally re-export to included C code, which includes some fast crypto code ported in on some platforms.
+// This eliminates the need to link against a third party SHA512() from this code
+extern "C" void ZT_sha512internal(void *digest,const void *data,unsigned int len)
+{
+ ZeroTier::SHA512::hash(digest,data,len);
+}
diff --git a/node/SHA512.hpp b/node/SHA512.hpp
index 639a7dfd..eedc284a 100644
--- a/node/SHA512.hpp
+++ b/node/SHA512.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_SHA512_HPP
diff --git a/node/Salsa20.hpp b/node/Salsa20.hpp
index 52592602..bfb6d9d9 100644
--- a/node/Salsa20.hpp
+++ b/node/Salsa20.hpp
@@ -48,6 +48,43 @@ public:
static inline void memxor(uint8_t *d,const uint8_t *s,unsigned int len)
{
#ifdef ZT_SALSA20_SSE
+ while (len >= 128) {
+ __m128i s0 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s));
+ __m128i s1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 16));
+ __m128i s2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 32));
+ __m128i s3 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 48));
+ __m128i s4 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 64));
+ __m128i s5 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 80));
+ __m128i s6 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 96));
+ __m128i s7 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 112));
+ __m128i d0 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d));
+ __m128i d1 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 16));
+ __m128i d2 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 32));
+ __m128i d3 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 48));
+ __m128i d4 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 64));
+ __m128i d5 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 80));
+ __m128i d6 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 96));
+ __m128i d7 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 112));
+ d0 = _mm_xor_si128(d0,s0);
+ d1 = _mm_xor_si128(d1,s1);
+ d2 = _mm_xor_si128(d2,s2);
+ d3 = _mm_xor_si128(d3,s3);
+ d4 = _mm_xor_si128(d4,s4);
+ d5 = _mm_xor_si128(d5,s5);
+ d6 = _mm_xor_si128(d6,s6);
+ d7 = _mm_xor_si128(d7,s7);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d),d0);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d + 16),d1);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d + 32),d2);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d + 48),d3);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d + 64),d4);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d + 80),d5);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d + 96),d6);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d + 112),d7);
+ s += 128;
+ d += 128;
+ len -= 128;
+ }
while (len >= 16) {
_mm_storeu_si128(reinterpret_cast<__m128i *>(d),_mm_xor_si128(_mm_loadu_si128(reinterpret_cast<__m128i *>(d)),_mm_loadu_si128(reinterpret_cast<const __m128i *>(s))));
s += 16;
@@ -67,8 +104,10 @@ public:
}
#endif
#endif
- while (len--)
+ while (len) {
+ --len;
*(d++) ^= *(s++);
+ }
}
/**
diff --git a/node/SelfAwareness.cpp b/node/SelfAwareness.cpp
index cba84cdc..c4f107fb 100644
--- a/node/SelfAwareness.cpp
+++ b/node/SelfAwareness.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -31,6 +39,7 @@
#include "Packet.hpp"
#include "Peer.hpp"
#include "Switch.hpp"
+#include "Trace.hpp"
// Entry timeout -- make it fairly long since this is just to prevent stale buildup
#define ZT_SELFAWARENESS_ENTRY_TIMEOUT 600000
@@ -40,7 +49,7 @@ namespace ZeroTier {
class _ResetWithinScope
{
public:
- _ResetWithinScope(void *tPtr,uint64_t now,int inetAddressFamily,InetAddress::IpScope scope) :
+ _ResetWithinScope(void *tPtr,int64_t now,int inetAddressFamily,InetAddress::IpScope scope) :
_now(now),
_tPtr(tPtr),
_family(inetAddressFamily),
@@ -61,7 +70,7 @@ SelfAwareness::SelfAwareness(const RuntimeEnvironment *renv) :
{
}
-void SelfAwareness::iam(void *tPtr,const Address &reporter,const InetAddress &receivedOnLocalAddress,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,uint64_t now)
+void SelfAwareness::iam(void *tPtr,const Address &reporter,const int64_t receivedOnLocalSocket,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,int64_t now)
{
const InetAddress::IpScope scope = myPhysicalAddress.ipScope();
@@ -69,11 +78,11 @@ void SelfAwareness::iam(void *tPtr,const Address &reporter,const InetAddress &re
return;
Mutex::Lock _l(_phy_m);
- PhySurfaceEntry &entry = _phy[PhySurfaceKey(reporter,receivedOnLocalAddress,reporterPhysicalAddress,scope)];
+ PhySurfaceEntry &entry = _phy[PhySurfaceKey(reporter,receivedOnLocalSocket,reporterPhysicalAddress,scope)];
if ( (trusted) && ((now - entry.ts) < ZT_SELFAWARENESS_ENTRY_TIMEOUT) && (!entry.mySurface.ipsEqual(myPhysicalAddress)) ) {
// Changes to external surface reported by trusted peers causes path reset in this scope
- TRACE("physical address %s for scope %u as seen from %s(%s) differs from %s, resetting paths in scope",myPhysicalAddress.toString().c_str(),(unsigned int)scope,reporter.toString().c_str(),reporterPhysicalAddress.toString().c_str(),entry.mySurface.toString().c_str());
+ RR->t->resettingPathsInScope(tPtr,reporter,reporterPhysicalAddress,myPhysicalAddress,scope);
entry.mySurface = myPhysicalAddress;
entry.ts = now;
@@ -103,7 +112,7 @@ void SelfAwareness::iam(void *tPtr,const Address &reporter,const InetAddress &re
}
}
-void SelfAwareness::clean(uint64_t now)
+void SelfAwareness::clean(int64_t now)
{
Mutex::Lock _l(_phy_m);
Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
@@ -138,13 +147,14 @@ std::vector<InetAddress> SelfAwareness::getSymmetricNatPredictions()
* read or modify traffic, but they could gather meta-data for forensics
* purpsoes or use this as a DOS attack vector. */
- std::map< uint32_t,std::pair<uint64_t,unsigned int> > maxPortByIp;
+ std::map< uint32_t,unsigned int > maxPortByIp;
InetAddress theOneTrueSurface;
- bool symmetric = false;
{
Mutex::Lock _l(_phy_m);
- { // First get IPs from only trusted peers, and perform basic NAT type characterization
+ // First check to see if this is a symmetric NAT and enumerate external IPs learned from trusted peers
+ bool symmetric = false;
+ {
Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
PhySurfaceKey *k = (PhySurfaceKey *)0;
PhySurfaceEntry *e = (PhySurfaceEntry *)0;
@@ -154,42 +164,47 @@ std::vector<InetAddress> SelfAwareness::getSymmetricNatPredictions()
theOneTrueSurface = e->mySurface;
else if (theOneTrueSurface != e->mySurface)
symmetric = true;
- maxPortByIp[reinterpret_cast<const struct sockaddr_in *>(&(e->mySurface))->sin_addr.s_addr] = std::pair<uint64_t,unsigned int>(e->ts,e->mySurface.port());
+ maxPortByIp[reinterpret_cast<const struct sockaddr_in *>(&(e->mySurface))->sin_addr.s_addr] = e->mySurface.port();
}
}
}
+ if (!symmetric)
+ return std::vector<InetAddress>();
- { // Then find max port per IP from a trusted peer
+ { // Then find the highest issued port per IP
Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
PhySurfaceKey *k = (PhySurfaceKey *)0;
PhySurfaceEntry *e = (PhySurfaceEntry *)0;
while (i.next(k,e)) {
if ((e->mySurface.ss_family == AF_INET)&&(e->mySurface.ipScope() == InetAddress::IP_SCOPE_GLOBAL)) {
- std::map< uint32_t,std::pair<uint64_t,unsigned int> >::iterator mp(maxPortByIp.find(reinterpret_cast<const struct sockaddr_in *>(&(e->mySurface))->sin_addr.s_addr));
- if ((mp != maxPortByIp.end())&&(mp->second.first < e->ts)) {
- mp->second.first = e->ts;
- mp->second.second = e->mySurface.port();
- }
+ const unsigned int port = e->mySurface.port();
+ std::map< uint32_t,unsigned int >::iterator mp(maxPortByIp.find(reinterpret_cast<const struct sockaddr_in *>(&(e->mySurface))->sin_addr.s_addr));
+ if ((mp != maxPortByIp.end())&&(mp->second < port))
+ mp->second = port;
}
}
}
}
- if (symmetric) {
- std::vector<InetAddress> r;
- for(unsigned int k=1;k<=3;++k) {
- for(std::map< uint32_t,std::pair<uint64_t,unsigned int> >::iterator i(maxPortByIp.begin());i!=maxPortByIp.end();++i) {
- unsigned int p = i->second.second + k;
- if (p > 65535) p -= 64511;
- InetAddress pred(&(i->first),4,p);
- if (std::find(r.begin(),r.end(),pred) == r.end())
- r.push_back(pred);
- }
- }
- return r;
+ std::vector<InetAddress> r;
+
+ // Try next port up from max for each
+ for(std::map< uint32_t,unsigned int >::iterator i(maxPortByIp.begin());i!=maxPortByIp.end();++i) {
+ unsigned int p = i->second + 1;
+ if (p > 65535) p -= 64511;
+ const InetAddress pred(&(i->first),4,p);
+ if (std::find(r.begin(),r.end(),pred) == r.end())
+ r.push_back(pred);
+ }
+
+ // Try a random port for each -- there are only 65535 so eventually it should work
+ for(std::map< uint32_t,unsigned int >::iterator i(maxPortByIp.begin());i!=maxPortByIp.end();++i) {
+ const InetAddress pred(&(i->first),4,1024 + ((unsigned int)RR->node->prng() % 64511));
+ if (std::find(r.begin(),r.end(),pred) == r.end())
+ r.push_back(pred);
}
- return std::vector<InetAddress>();
+ return r;
}
} // namespace ZeroTier
diff --git a/node/SelfAwareness.hpp b/node/SelfAwareness.hpp
index c1db0c84..ce6e8c76 100644
--- a/node/SelfAwareness.hpp
+++ b/node/SelfAwareness.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_SELFAWARENESS_HPP
@@ -47,14 +55,14 @@ public:
* @param trusted True if this peer is trusted as an authority to inform us of external address changes
* @param now Current time
*/
- void iam(void *tPtr,const Address &reporter,const InetAddress &receivedOnLocalAddress,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,uint64_t now);
+ void iam(void *tPtr,const Address &reporter,const int64_t receivedOnLocalSocket,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,int64_t now);
/**
* Clean up database periodically
*
* @param now Current time
*/
- void clean(uint64_t now);
+ void clean(int64_t now);
/**
* If we appear to be behind a symmetric NAT, get predictions for possible external endpoints
@@ -67,15 +75,15 @@ private:
struct PhySurfaceKey
{
Address reporter;
- InetAddress receivedOnLocalAddress;
+ int64_t receivedOnLocalSocket;
InetAddress reporterPhysicalAddress;
InetAddress::IpScope scope;
PhySurfaceKey() : reporter(),scope(InetAddress::IP_SCOPE_NONE) {}
- PhySurfaceKey(const Address &r,const InetAddress &rol,const InetAddress &ra,InetAddress::IpScope s) : reporter(r),receivedOnLocalAddress(rol),reporterPhysicalAddress(ra),scope(s) {}
+ PhySurfaceKey(const Address &r,const int64_t rol,const InetAddress &ra,InetAddress::IpScope s) : reporter(r),receivedOnLocalSocket(rol),reporterPhysicalAddress(ra),scope(s) {}
- inline unsigned long hashCode() const throw() { return ((unsigned long)reporter.toInt() + (unsigned long)scope); }
- inline bool operator==(const PhySurfaceKey &k) const throw() { return ((reporter == k.reporter)&&(receivedOnLocalAddress == k.receivedOnLocalAddress)&&(reporterPhysicalAddress == k.reporterPhysicalAddress)&&(scope == k.scope)); }
+ inline unsigned long hashCode() const { return ((unsigned long)reporter.toInt() + (unsigned long)scope); }
+ inline bool operator==(const PhySurfaceKey &k) const { return ((reporter == k.reporter)&&(receivedOnLocalSocket == k.receivedOnLocalSocket)&&(reporterPhysicalAddress == k.reporterPhysicalAddress)&&(scope == k.scope)); }
};
struct PhySurfaceEntry
{
diff --git a/node/SharedPtr.hpp b/node/SharedPtr.hpp
index 1dd3b43d..2f0e50c9 100644
--- a/node/SharedPtr.hpp
+++ b/node/SharedPtr.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_SHAREDPTR_HPP
@@ -25,41 +33,19 @@
namespace ZeroTier {
/**
- * Simple reference counted pointer
+ * Simple zero-overhead introspective reference counted pointer
*
* This is an introspective shared pointer. Classes that need to be reference
* counted must list this as a 'friend' and must have a private instance of
- * AtomicCounter called __refCount. They should also have private destructors,
- * since only this class should delete them.
- *
- * Because this is introspective, it is safe to apply to a naked pointer
- * multiple times provided there is always at least one holding SharedPtr.
- *
- * Once C++11 is ubiquitous, this and a few other things like Thread might get
- * torn out for their standard equivalents.
+ * AtomicCounter called __refCount.
*/
template<typename T>
class SharedPtr
{
public:
- SharedPtr()
- throw() :
- _ptr((T *)0)
- {
- }
-
- SharedPtr(T *obj)
- throw() :
- _ptr(obj)
- {
- ++obj->__refCount;
- }
-
- SharedPtr(const SharedPtr &sp)
- throw() :
- _ptr(sp._getAndInc())
- {
- }
+ SharedPtr() : _ptr((T *)0) {}
+ SharedPtr(T *obj) : _ptr(obj) { ++obj->__refCount; }
+ SharedPtr(const SharedPtr &sp) : _ptr(sp._getAndInc()) {}
~SharedPtr()
{
@@ -90,8 +76,9 @@ public:
*
* @param ptr Naked pointer to assign
*/
- inline void setToUnsafe(T *ptr)
+ inline void set(T *ptr)
{
+ zero();
++ptr->__refCount;
_ptr = ptr;
}
@@ -102,21 +89,20 @@ public:
* @param with Pointer to swap with
*/
inline void swap(SharedPtr &with)
- throw()
{
T *tmp = _ptr;
_ptr = with._ptr;
with._ptr = tmp;
}
- inline operator bool() const throw() { return (_ptr != (T *)0); }
- inline T &operator*() const throw() { return *_ptr; }
- inline T *operator->() const throw() { return _ptr; }
+ inline operator bool() const { return (_ptr != (T *)0); }
+ inline T &operator*() const { return *_ptr; }
+ inline T *operator->() const { return _ptr; }
/**
* @return Raw pointer to held object
*/
- inline T *ptr() const throw() { return _ptr; }
+ inline T *ptr() const { return _ptr; }
/**
* Set this pointer to NULL
@@ -131,45 +117,29 @@ public:
}
/**
- * Set this pointer to NULL if this is the only pointer holding the object
- *
- * @return True if object was deleted and SharedPtr is now NULL (or was already NULL)
+ * @return Number of references according to this object's ref count or 0 if NULL
*/
- inline bool reclaimIfWeak()
+ inline int references()
{
- if (_ptr) {
- if (++_ptr->__refCount <= 2) {
- if (--_ptr->__refCount <= 1) {
- delete _ptr;
- _ptr = (T *)0;
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- } else {
- return true;
- }
+ if (_ptr)
+ return _ptr->__refCount.load();
+ return 0;
}
- inline bool operator==(const SharedPtr &sp) const throw() { return (_ptr == sp._ptr); }
- inline bool operator!=(const SharedPtr &sp) const throw() { return (_ptr != sp._ptr); }
- inline bool operator>(const SharedPtr &sp) const throw() { return (_ptr > sp._ptr); }
- inline bool operator<(const SharedPtr &sp) const throw() { return (_ptr < sp._ptr); }
- inline bool operator>=(const SharedPtr &sp) const throw() { return (_ptr >= sp._ptr); }
- inline bool operator<=(const SharedPtr &sp) const throw() { return (_ptr <= sp._ptr); }
+ inline bool operator==(const SharedPtr &sp) const { return (_ptr == sp._ptr); }
+ inline bool operator!=(const SharedPtr &sp) const { return (_ptr != sp._ptr); }
+ inline bool operator>(const SharedPtr &sp) const { return (_ptr > sp._ptr); }
+ inline bool operator<(const SharedPtr &sp) const { return (_ptr < sp._ptr); }
+ inline bool operator>=(const SharedPtr &sp) const { return (_ptr >= sp._ptr); }
+ inline bool operator<=(const SharedPtr &sp) const { return (_ptr <= sp._ptr); }
private:
inline T *_getAndInc() const
- throw()
{
if (_ptr)
++_ptr->__refCount;
return _ptr;
}
-
T *_ptr;
};
diff --git a/node/Switch.cpp b/node/Switch.cpp
index 56299a9a..eb1ebadb 100644
--- a/node/Switch.cpp
+++ b/node/Switch.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -35,41 +43,24 @@
#include "Peer.hpp"
#include "SelfAwareness.hpp"
#include "Packet.hpp"
-#include "Cluster.hpp"
+#include "Trace.hpp"
namespace ZeroTier {
-#ifdef ZT_TRACE
-static const char *etherTypeName(const unsigned int etherType)
-{
- switch(etherType) {
- case ZT_ETHERTYPE_IPV4: return "IPV4";
- case ZT_ETHERTYPE_ARP: return "ARP";
- case ZT_ETHERTYPE_RARP: return "RARP";
- case ZT_ETHERTYPE_ATALK: return "ATALK";
- case ZT_ETHERTYPE_AARP: return "AARP";
- case ZT_ETHERTYPE_IPX_A: return "IPX_A";
- case ZT_ETHERTYPE_IPX_B: return "IPX_B";
- case ZT_ETHERTYPE_IPV6: return "IPV6";
- }
- return "UNKNOWN";
-}
-#endif // ZT_TRACE
-
Switch::Switch(const RuntimeEnvironment *renv) :
RR(renv),
_lastBeaconResponse(0),
- _outstandingWhoisRequests(32),
+ _lastCheckedQueues(0),
_lastUniteAttempt(8) // only really used on root servers and upstreams, and it'll grow there just fine
{
}
-void Switch::onRemotePacket(void *tPtr,const InetAddress &localAddr,const InetAddress &fromAddr,const void *data,unsigned int len)
+void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddress &fromAddr,const void *data,unsigned int len)
{
try {
- const uint64_t now = RR->node->now();
+ const int64_t now = RR->node->now();
- SharedPtr<Path> path(RR->topology->getPath(localAddr,fromAddr));
+ const SharedPtr<Path> path(RR->topology->getPath(localSocket,fromAddr));
path->received(now);
if (len == 13) {
@@ -81,14 +72,14 @@ void Switch::onRemotePacket(void *tPtr,const InetAddress &localAddr,const InetAd
const Address beaconAddr(reinterpret_cast<const char *>(data) + 8,5);
if (beaconAddr == RR->identity.address())
return;
- if (!RR->node->shouldUsePathForZeroTierTraffic(tPtr,beaconAddr,localAddr,fromAddr))
+ if (!RR->node->shouldUsePathForZeroTierTraffic(tPtr,beaconAddr,localSocket,fromAddr))
return;
const SharedPtr<Peer> peer(RR->topology->getPeer(tPtr,beaconAddr));
if (peer) { // we'll only respond to beacons from known peers
if ((now - _lastBeaconResponse) >= 2500) { // limit rate of responses
_lastBeaconResponse = now;
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NOP);
- outp.armor(peer->key(),true,path->nextOutgoingCounter());
+ outp.armor(peer->key(),true);
path->send(RR,tPtr,outp.data(),outp.size(),now);
}
}
@@ -101,13 +92,7 @@ void Switch::onRemotePacket(void *tPtr,const InetAddress &localAddr,const InetAd
const Address destination(fragment.destination());
if (destination != RR->identity.address()) {
-#ifdef ZT_ENABLE_CLUSTER
- const bool isClusterFrontplane = ((RR->cluster)&&(RR->cluster->isClusterPeerFrontplane(fromAddr)));
-#else
- const bool isClusterFrontplane = false;
-#endif
-
- if ( (!RR->topology->amRoot()) && (!path->trustEstablished(now)) && (!isClusterFrontplane) )
+ if ( (!RR->topology->amUpstream()) && (!path->trustEstablished(now)) )
return;
if (fragment.hops() < ZT_RELAY_MAX_HOPS) {
@@ -117,20 +102,11 @@ void Switch::onRemotePacket(void *tPtr,const InetAddress &localAddr,const InetAd
// It wouldn't hurt anything, just redundant and unnecessary.
SharedPtr<Peer> relayTo = RR->topology->getPeer(tPtr,destination);
if ((!relayTo)||(!relayTo->sendDirect(tPtr,fragment.data(),fragment.size(),now,false))) {
-#ifdef ZT_ENABLE_CLUSTER
- if ((RR->cluster)&&(!isClusterFrontplane)) {
- RR->cluster->relayViaCluster(Address(),destination,fragment.data(),fragment.size(),false);
- return;
- }
-#endif
-
// Don't know peer or no direct path -- so relay via someone upstream
relayTo = RR->topology->getUpstreamPeer();
if (relayTo)
relayTo->sendDirect(tPtr,fragment.data(),fragment.size(),now,true);
}
- } else {
- TRACE("dropped relay [fragment](%s) -> %s, max hops exceeded",fromAddr.toString().c_str(),destination.toString().c_str());
}
} else {
// Fragment looks like ours
@@ -144,12 +120,9 @@ void Switch::onRemotePacket(void *tPtr,const InetAddress &localAddr,const InetAd
// Total fragments must be more than 1, otherwise why are we
// seeing a Packet::Fragment?
- Mutex::Lock _l(_rxQueue_m);
- RXQueueEntry *const rq = _findRXQueueEntry(now,fragmentPacketId);
-
- if ((!rq->timestamp)||(rq->packetId != fragmentPacketId)) {
+ RXQueueEntry *const rq = _findRXQueueEntry(fragmentPacketId);
+ if (rq->packetId != fragmentPacketId) {
// No packet found, so we received a fragment without its head.
- //TRACE("fragment (%u/%u) of %.16llx from %s",fragmentNumber + 1,totalFragments,fragmentPacketId,fromAddr.toString().c_str());
rq->timestamp = now;
rq->packetId = fragmentPacketId;
@@ -159,14 +132,12 @@ void Switch::onRemotePacket(void *tPtr,const InetAddress &localAddr,const InetAd
rq->complete = false;
} else if (!(rq->haveFragments & (1 << fragmentNumber))) {
// We have other fragments and maybe the head, so add this one and check
- //TRACE("fragment (%u/%u) of %.16llx from %s",fragmentNumber + 1,totalFragments,fragmentPacketId,fromAddr.toString().c_str());
rq->frags[fragmentNumber - 1] = fragment;
rq->totalFragments = totalFragments;
if (Utils::countBits(rq->haveFragments |= (1 << fragmentNumber)) == totalFragments) {
// We have all fragments -- assemble and process full Packet
- //TRACE("packet %.16llx is complete, assembling and processing...",fragmentPacketId);
for(unsigned int f=1;f<totalFragments;++f)
rq->frag0.append(rq->frags[f - 1].payload(),rq->frags[f - 1].payloadLength());
@@ -188,100 +159,34 @@ void Switch::onRemotePacket(void *tPtr,const InetAddress &localAddr,const InetAd
const Address destination(reinterpret_cast<const uint8_t *>(data) + 8,ZT_ADDRESS_LENGTH);
const Address source(reinterpret_cast<const uint8_t *>(data) + 13,ZT_ADDRESS_LENGTH);
- //TRACE("<< %.16llx %s -> %s (size: %u)",(unsigned long long)packet->packetId(),source.toString().c_str(),destination.toString().c_str(),packet->size());
-
-#ifdef ZT_ENABLE_CLUSTER
- if ( (source == RR->identity.address()) && ((!RR->cluster)||(!RR->cluster->isClusterPeerFrontplane(fromAddr))) )
- return;
-#else
if (source == RR->identity.address())
return;
-#endif
if (destination != RR->identity.address()) {
- if ( (!RR->topology->amRoot()) && (!path->trustEstablished(now)) && (source != RR->identity.address()) )
+ if ( (!RR->topology->amUpstream()) && (!path->trustEstablished(now)) && (source != RR->identity.address()) )
return;
Packet packet(data,len);
if (packet.hops() < ZT_RELAY_MAX_HOPS) {
-#ifdef ZT_ENABLE_CLUSTER
- if (source != RR->identity.address()) // don't increment hops for cluster frontplane relays
- packet.incrementHops();
-#else
packet.incrementHops();
-#endif
-
SharedPtr<Peer> relayTo = RR->topology->getPeer(tPtr,destination);
if ((relayTo)&&(relayTo->sendDirect(tPtr,packet.data(),packet.size(),now,false))) {
- if ((source != RR->identity.address())&&(_shouldUnite(now,source,destination))) { // don't send RENDEZVOUS for cluster frontplane relays
- const InetAddress *hintToSource = (InetAddress *)0;
- const InetAddress *hintToDest = (InetAddress *)0;
-
- InetAddress destV4,destV6;
- InetAddress sourceV4,sourceV6;
- relayTo->getRendezvousAddresses(now,destV4,destV6);
-
+ if ((source != RR->identity.address())&&(_shouldUnite(now,source,destination))) {
const SharedPtr<Peer> sourcePeer(RR->topology->getPeer(tPtr,source));
- if (sourcePeer) {
- sourcePeer->getRendezvousAddresses(now,sourceV4,sourceV6);
- if ((destV6)&&(sourceV6)) {
- hintToSource = &destV6;
- hintToDest = &sourceV6;
- } else if ((destV4)&&(sourceV4)) {
- hintToSource = &destV4;
- hintToDest = &sourceV4;
- }
-
- if ((hintToSource)&&(hintToDest)) {
- unsigned int alt = (unsigned int)RR->node->prng() & 1; // randomize which hint we send first for obscure NAT-t reasons
- const unsigned int completed = alt + 2;
- while (alt != completed) {
- if ((alt & 1) == 0) {
- Packet outp(source,RR->identity.address(),Packet::VERB_RENDEZVOUS);
- outp.append((uint8_t)0);
- destination.appendTo(outp);
- outp.append((uint16_t)hintToSource->port());
- if (hintToSource->ss_family == AF_INET6) {
- outp.append((uint8_t)16);
- outp.append(hintToSource->rawIpData(),16);
- } else {
- outp.append((uint8_t)4);
- outp.append(hintToSource->rawIpData(),4);
- }
- send(tPtr,outp,true);
- } else {
- Packet outp(destination,RR->identity.address(),Packet::VERB_RENDEZVOUS);
- outp.append((uint8_t)0);
- source.appendTo(outp);
- outp.append((uint16_t)hintToDest->port());
- if (hintToDest->ss_family == AF_INET6) {
- outp.append((uint8_t)16);
- outp.append(hintToDest->rawIpData(),16);
- } else {
- outp.append((uint8_t)4);
- outp.append(hintToDest->rawIpData(),4);
- }
- send(tPtr,outp,true);
- }
- ++alt;
- }
- }
- }
+ if (sourcePeer)
+ relayTo->introduce(tPtr,now,sourcePeer);
}
} else {
-#ifdef ZT_ENABLE_CLUSTER
- if ((RR->cluster)&&(source != RR->identity.address())) {
- RR->cluster->relayViaCluster(source,destination,packet.data(),packet.size(),_shouldUnite(now,source,destination));
- return;
+ relayTo = RR->topology->getUpstreamPeer();
+ if ((relayTo)&&(relayTo->address() != source)) {
+ if (relayTo->sendDirect(tPtr,packet.data(),packet.size(),now,true)) {
+ const SharedPtr<Peer> sourcePeer(RR->topology->getPeer(tPtr,source));
+ if (sourcePeer)
+ relayTo->introduce(tPtr,now,sourcePeer);
+ }
}
-#endif
- relayTo = RR->topology->getUpstreamPeer(&source,1,true);
- if (relayTo)
- relayTo->sendDirect(tPtr,packet.data(),packet.size(),now,true);
}
- } else {
- TRACE("dropped relay %s(%s) -> %s, max hops exceeded",packet.source().toString().c_str(),fromAddr.toString().c_str(),destination.toString().c_str());
}
} else if ((reinterpret_cast<const uint8_t *>(data)[ZT_PACKET_IDX_FLAGS] & ZT_PROTO_FLAG_FRAGMENTED) != 0) {
// Packet is the head of a fragmented packet series
@@ -297,12 +202,9 @@ void Switch::onRemotePacket(void *tPtr,const InetAddress &localAddr,const InetAd
((uint64_t)reinterpret_cast<const uint8_t *>(data)[7])
);
- Mutex::Lock _l(_rxQueue_m);
- RXQueueEntry *const rq = _findRXQueueEntry(now,packetId);
-
- if ((!rq->timestamp)||(rq->packetId != packetId)) {
+ RXQueueEntry *const rq = _findRXQueueEntry(packetId);
+ if (rq->packetId != packetId) {
// If we have no other fragments yet, create an entry and save the head
- //TRACE("fragment (0/?) of %.16llx from %s",pid,fromAddr.toString().c_str());
rq->timestamp = now;
rq->packetId = packetId;
@@ -315,7 +217,6 @@ void Switch::onRemotePacket(void *tPtr,const InetAddress &localAddr,const InetAd
if ((rq->totalFragments > 1)&&(Utils::countBits(rq->haveFragments |= 1) == rq->totalFragments)) {
// We have all fragments -- assemble and process full Packet
- //TRACE("packet %.16llx is complete, assembling and processing...",pid);
rq->frag0.init(data,len,path,now);
for(unsigned int f=1;f<rq->totalFragments;++f)
@@ -335,14 +236,7 @@ void Switch::onRemotePacket(void *tPtr,const InetAddress &localAddr,const InetAd
// Packet is unfragmented, so just process it
IncomingPacket packet(data,len,path,now);
if (!packet.tryDecode(RR,tPtr)) {
- Mutex::Lock _l(_rxQueue_m);
- RXQueueEntry *rq = &(_rxQueue[ZT_RX_QUEUE_SIZE - 1]);
- unsigned long i = ZT_RX_QUEUE_SIZE - 1;
- while ((i)&&(rq->timestamp)) {
- RXQueueEntry *tmp = &(_rxQueue[--i]);
- if (tmp->timestamp < rq->timestamp)
- rq = tmp;
- }
+ RXQueueEntry *const rq = _nextRXQueueEntry();
rq->timestamp = now;
rq->packetId = packet.packetId();
rq->frag0 = packet;
@@ -355,11 +249,7 @@ void Switch::onRemotePacket(void *tPtr,const InetAddress &localAddr,const InetAd
// --------------------------------------------------------------------
}
}
- } catch (std::exception &ex) {
- TRACE("dropped packet from %s: unexpected exception: %s",fromAddr.toString().c_str(),ex.what());
- } catch ( ... ) {
- TRACE("dropped packet from %s: unexpected exception: (unknown)",fromAddr.toString().c_str());
- }
+ } catch ( ... ) {} // sanity check, should be caught elsewhere
}
void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
@@ -371,7 +261,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
bool fromBridged;
if ((fromBridged = (from != network->mac()))) {
if (!network->config().permitsBridging(RR->identity.address())) {
- TRACE("%.16llx: %s -> %s %s not forwarded, bridging disabled or this peer not a bridge",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
+ RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"not a bridge");
return;
}
}
@@ -393,7 +283,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
multicastGroup = MulticastGroup::deriveMulticastGroupForAddressResolution(InetAddress(((const unsigned char *)data) + 24,4,0));
} else if (!network->config().enableBroadcast()) {
// Don't transmit broadcasts if this network doesn't want them
- TRACE("%.16llx: dropped broadcast since ff:ff:ff:ff:ff:ff is not enabled",network->id());
+ RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"broadcast disabled");
return;
}
} else if ((etherType == ZT_ETHERTYPE_IPV6)&&(len >= (40 + 8 + 16))) {
@@ -446,7 +336,6 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
if ((v6EmbeddedAddress)&&(v6EmbeddedAddress != RR->identity.address())) {
const MAC peerMac(v6EmbeddedAddress,network->id());
- TRACE("IPv6 NDP emulation: %.16llx: forging response for %s/%s",network->id(),v6EmbeddedAddress.toString().c_str(),peerMac.toString().c_str());
uint8_t adv[72];
adv[0] = 0x60; adv[1] = 0x00; adv[2] = 0x00; adv[3] = 0x00;
@@ -482,7 +371,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
// Check this after NDP emulation, since that has to be allowed in exactly this case
if (network->config().multicastLimit == 0) {
- TRACE("%.16llx: dropped multicast: not allowed on network",network->id());
+ RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"multicast disabled");
return;
}
@@ -493,21 +382,17 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
if (fromBridged)
network->learnBridgedMulticastGroup(tPtr,multicastGroup,RR->node->now());
- //TRACE("%.16llx: MULTICAST %s -> %s %s %u",network->id(),from.toString().c_str(),multicastGroup.toString().c_str(),etherTypeName(etherType),len);
-
// First pass sets noTee to false, but noTee is set to true in OutboundMulticast to prevent duplicates.
if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId)) {
- TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
+ RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"filter blocked");
return;
}
RR->mc->send(
tPtr,
- network->config().multicastLimit,
RR->node->now(),
- network->id(),
- network->config().disableCompression(),
- network->config().activeBridges(),
+ network,
+ Address(),
multicastGroup,
(fromBridged) ? from : MAC(),
etherType,
@@ -523,7 +408,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
SharedPtr<Peer> toPeer(RR->topology->getPeer(tPtr,toZT));
if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),toZT,from,to,(const uint8_t *)data,len,etherType,vlanId)) {
- TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
+ RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"filter blocked");
return;
}
@@ -548,7 +433,6 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
send(tPtr,outp,true);
}
- //TRACE("%.16llx: UNICAST: %s -> %s etherType==%s(%.4x) vlanId==%u len==%u fromBridged==%d includeCom==%d",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),etherType,vlanId,len,(int)fromBridged,(int)includeCom);
} else {
// Destination is bridged behind a remote peer
@@ -556,7 +440,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
// for each ZT destination are also done below. This is the same rationale
// and design as for multicast.
if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId)) {
- TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
+ RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"filter blocked");
return;
}
@@ -605,7 +489,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
outp.compress();
send(tPtr,outp,true);
} else {
- TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
+ RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"filter blocked (bridge replication)");
}
}
}
@@ -613,113 +497,112 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
void Switch::send(void *tPtr,Packet &packet,bool encrypt)
{
- if (packet.destination() == RR->identity.address()) {
- TRACE("BUG: caught attempt to send() to self, ignored");
+ const Address dest(packet.destination());
+ if (dest == RR->identity.address())
return;
- }
-
if (!_trySend(tPtr,packet,encrypt)) {
- Mutex::Lock _l(_txQueue_m);
- _txQueue.push_back(TXQueueEntry(packet.destination(),RR->node->now(),packet,encrypt));
+ {
+ Mutex::Lock _l(_txQueue_m);
+ _txQueue.push_back(TXQueueEntry(dest,RR->node->now(),packet,encrypt));
+ }
+ if (!RR->topology->getPeer(tPtr,dest))
+ requestWhois(tPtr,RR->node->now(),dest);
}
}
-void Switch::requestWhois(void *tPtr,const Address &addr)
+void Switch::requestWhois(void *tPtr,const int64_t now,const Address &addr)
{
-#ifdef ZT_TRACE
- if (addr == RR->identity.address()) {
- fprintf(stderr,"FATAL BUG: Switch::requestWhois() caught attempt to WHOIS self" ZT_EOL_S);
- abort();
- }
-#endif
+ if (addr == RR->identity.address())
+ return;
- bool inserted = false;
{
- Mutex::Lock _l(_outstandingWhoisRequests_m);
- WhoisRequest &r = _outstandingWhoisRequests[addr];
- if (r.lastSent) {
- r.retries = 0; // reset retry count if entry already existed, but keep waiting and retry again after normal timeout
- } else {
- r.lastSent = RR->node->now();
- inserted = true;
- }
+ Mutex::Lock _l(_lastSentWhoisRequest_m);
+ int64_t &last = _lastSentWhoisRequest[addr];
+ if ((now - last) < ZT_WHOIS_RETRY_DELAY)
+ return;
+ else last = now;
+ }
+
+ const SharedPtr<Peer> upstream(RR->topology->getUpstreamPeer());
+ if (upstream) {
+ Packet outp(upstream->address(),RR->identity.address(),Packet::VERB_WHOIS);
+ addr.appendTo(outp);
+ RR->node->expectReplyTo(outp.packetId());
+ send(tPtr,outp,true);
}
- if (inserted)
- _sendWhoisRequest(tPtr,addr,(const Address *)0,0);
}
void Switch::doAnythingWaitingForPeer(void *tPtr,const SharedPtr<Peer> &peer)
{
- { // cancel pending WHOIS since we now know this peer
- Mutex::Lock _l(_outstandingWhoisRequests_m);
- _outstandingWhoisRequests.erase(peer->address());
+ {
+ Mutex::Lock _l(_lastSentWhoisRequest_m);
+ _lastSentWhoisRequest.erase(peer->address());
}
- { // finish processing any packets waiting on peer's public key / identity
- Mutex::Lock _l(_rxQueue_m);
- unsigned long i = ZT_RX_QUEUE_SIZE;
- while (i) {
- RXQueueEntry *rq = &(_rxQueue[--i]);
- if ((rq->timestamp)&&(rq->complete)) {
- if (rq->frag0.tryDecode(RR,tPtr))
- rq->timestamp = 0;
- }
+ const int64_t now = RR->node->now();
+ for(unsigned int ptr=0;ptr<ZT_RX_QUEUE_SIZE;++ptr) {
+ RXQueueEntry *const rq = &(_rxQueue[ptr]);
+ if ((rq->timestamp)&&(rq->complete)) {
+ if ((rq->frag0.tryDecode(RR,tPtr))||((now - rq->timestamp) > ZT_RECEIVE_QUEUE_TIMEOUT))
+ rq->timestamp = 0;
}
}
- { // finish sending any packets waiting on peer's public key / identity
+ {
Mutex::Lock _l(_txQueue_m);
for(std::list< TXQueueEntry >::iterator txi(_txQueue.begin());txi!=_txQueue.end();) {
if (txi->dest == peer->address()) {
- if (_trySend(tPtr,txi->packet,txi->encrypt))
+ if (_trySend(tPtr,txi->packet,txi->encrypt)) {
_txQueue.erase(txi++);
- else ++txi;
- } else ++txi;
- }
- }
-}
-
-unsigned long Switch::doTimerTasks(void *tPtr,uint64_t now)
-{
- unsigned long nextDelay = 0xffffffff; // ceiling delay, caller will cap to minimum
-
- { // Retry outstanding WHOIS requests
- Mutex::Lock _l(_outstandingWhoisRequests_m);
- Hashtable< Address,WhoisRequest >::Iterator i(_outstandingWhoisRequests);
- Address *a = (Address *)0;
- WhoisRequest *r = (WhoisRequest *)0;
- while (i.next(a,r)) {
- const unsigned long since = (unsigned long)(now - r->lastSent);
- if (since >= ZT_WHOIS_RETRY_DELAY) {
- if (r->retries >= ZT_MAX_WHOIS_RETRIES) {
- TRACE("WHOIS %s timed out",a->toString().c_str());
- _outstandingWhoisRequests.erase(*a);
} else {
- r->lastSent = now;
- r->peersConsulted[r->retries] = _sendWhoisRequest(tPtr,*a,r->peersConsulted,(r->retries > 1) ? r->retries : 0);
- TRACE("WHOIS %s (retry %u)",a->toString().c_str(),r->retries);
- ++r->retries;
- nextDelay = std::min(nextDelay,(unsigned long)ZT_WHOIS_RETRY_DELAY);
+ ++txi;
}
} else {
- nextDelay = std::min(nextDelay,ZT_WHOIS_RETRY_DELAY - since);
+ ++txi;
}
}
}
+}
- { // Time out TX queue packets that never got WHOIS lookups or other info.
+unsigned long Switch::doTimerTasks(void *tPtr,int64_t now)
+{
+ const uint64_t timeSinceLastCheck = now - _lastCheckedQueues;
+ if (timeSinceLastCheck < ZT_WHOIS_RETRY_DELAY)
+ return (unsigned long)(ZT_WHOIS_RETRY_DELAY - timeSinceLastCheck);
+ _lastCheckedQueues = now;
+
+ std::vector<Address> needWhois;
+ {
Mutex::Lock _l(_txQueue_m);
for(std::list< TXQueueEntry >::iterator txi(_txQueue.begin());txi!=_txQueue.end();) {
- if (_trySend(tPtr,txi->packet,txi->encrypt))
+ if (_trySend(tPtr,txi->packet,txi->encrypt)) {
_txQueue.erase(txi++);
- else if ((now - txi->creationTime) > ZT_TRANSMIT_QUEUE_TIMEOUT) {
- TRACE("TX %s -> %s timed out",txi->packet.source().toString().c_str(),txi->packet.destination().toString().c_str());
+ } else if ((now - txi->creationTime) > ZT_TRANSMIT_QUEUE_TIMEOUT) {
_txQueue.erase(txi++);
- } else ++txi;
+ } else {
+ if (!RR->topology->getPeer(tPtr,txi->dest))
+ needWhois.push_back(txi->dest);
+ ++txi;
+ }
+ }
+ }
+ for(std::vector<Address>::const_iterator i(needWhois.begin());i!=needWhois.end();++i)
+ requestWhois(tPtr,now,*i);
+
+ for(unsigned int ptr=0;ptr<ZT_RX_QUEUE_SIZE;++ptr) {
+ RXQueueEntry *const rq = &(_rxQueue[ptr]);
+ if ((rq->timestamp)&&(rq->complete)) {
+ if ((rq->frag0.tryDecode(RR,tPtr))||((now - rq->timestamp) > ZT_RECEIVE_QUEUE_TIMEOUT)) {
+ rq->timestamp = 0;
+ } else {
+ const Address src(rq->frag0.source());
+ if (!RR->topology->getPeer(tPtr,src))
+ requestWhois(tPtr,now,src);
+ }
}
}
- { // Remove really old last unite attempt entries to keep table size controlled
+ {
Mutex::Lock _l(_lastUniteAttempt_m);
Hashtable< _LastUniteKey,uint64_t >::Iterator i(_lastUniteAttempt);
_LastUniteKey *k = (_LastUniteKey *)0;
@@ -730,10 +613,21 @@ unsigned long Switch::doTimerTasks(void *tPtr,uint64_t now)
}
}
- return nextDelay;
+ {
+ Mutex::Lock _l(_lastSentWhoisRequest_m);
+ Hashtable< Address,int64_t >::Iterator i(_lastSentWhoisRequest);
+ Address *a = (Address *)0;
+ int64_t *ts = (int64_t *)0;
+ while (i.next(a,ts)) {
+ if ((now - *ts) > (ZT_WHOIS_RETRY_DELAY * 2))
+ _lastSentWhoisRequest.erase(*a);
+ }
+ }
+
+ return ZT_WHOIS_RETRY_DELAY;
}
-bool Switch::_shouldUnite(const uint64_t now,const Address &source,const Address &destination)
+bool Switch::_shouldUnite(const int64_t now,const Address &source,const Address &destination)
{
Mutex::Lock _l(_lastUniteAttempt_m);
uint64_t &ts = _lastUniteAttempt[_LastUniteKey(source,destination)];
@@ -744,131 +638,54 @@ bool Switch::_shouldUnite(const uint64_t now,const Address &source,const Address
return false;
}
-Address Switch::_sendWhoisRequest(void *tPtr,const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted)
-{
- SharedPtr<Peer> upstream(RR->topology->getUpstreamPeer(peersAlreadyConsulted,numPeersAlreadyConsulted,false));
- if (upstream) {
- Packet outp(upstream->address(),RR->identity.address(),Packet::VERB_WHOIS);
- addr.appendTo(outp);
- RR->node->expectReplyTo(outp.packetId());
- send(tPtr,outp,true);
- }
- return Address();
-}
-
bool Switch::_trySend(void *tPtr,Packet &packet,bool encrypt)
{
SharedPtr<Path> viaPath;
- const uint64_t now = RR->node->now();
+ const int64_t now = RR->node->now();
const Address destination(packet.destination());
-#ifdef ZT_ENABLE_CLUSTER
- uint64_t clusterMostRecentTs = 0;
- int clusterMostRecentMemberId = -1;
- uint8_t clusterPeerSecret[ZT_PEER_SECRET_KEY_LENGTH];
- if (RR->cluster)
- clusterMostRecentMemberId = RR->cluster->checkSendViaCluster(destination,clusterMostRecentTs,clusterPeerSecret);
-#endif
-
const SharedPtr<Peer> peer(RR->topology->getPeer(tPtr,destination));
if (peer) {
- /* First get the best path, and if it's dead (and this is not a root)
- * we attempt to re-activate that path but this packet will flow
- * upstream. If the path comes back alive, it will be used in the future.
- * For roots we don't do the alive check since roots are not required
- * to send heartbeats "down" and because we have to at least try to
- * go somewhere. */
-
viaPath = peer->getBestPath(now,false);
- if ( (viaPath) && (!viaPath->alive(now)) && (!RR->topology->isUpstream(peer->identity())) ) {
-#ifdef ZT_ENABLE_CLUSTER
- if ((clusterMostRecentMemberId < 0)||(viaPath->lastIn() > clusterMostRecentTs)) {
-#endif
- if ((now - viaPath->lastOut()) > std::max((now - viaPath->lastIn()) * 4,(uint64_t)ZT_PATH_MIN_REACTIVATE_INTERVAL)) {
- peer->attemptToContactAt(tPtr,viaPath->localAddress(),viaPath->address(),now,false,viaPath->nextOutgoingCounter());
- viaPath->sent(now);
- }
-#ifdef ZT_ENABLE_CLUSTER
- }
-#endif
- viaPath.zero();
- }
-
-#ifdef ZT_ENABLE_CLUSTER
- if (clusterMostRecentMemberId >= 0) {
- if ((viaPath)&&(viaPath->lastIn() < clusterMostRecentTs))
- viaPath.zero();
- } else if (!viaPath) {
-#else
if (!viaPath) {
-#endif
peer->tryMemorizedPath(tPtr,now); // periodically attempt memorized or statically defined paths, if any are known
const SharedPtr<Peer> relay(RR->topology->getUpstreamPeer());
if ( (!relay) || (!(viaPath = relay->getBestPath(now,false))) ) {
if (!(viaPath = peer->getBestPath(now,true)))
return false;
}
-#ifdef ZT_ENABLE_CLUSTER
}
-#else
- }
-#endif
} else {
-#ifdef ZT_ENABLE_CLUSTER
- if (clusterMostRecentMemberId < 0) {
-#else
- requestWhois(tPtr,destination);
- return false; // if we are not in cluster mode, there is no way we can send without knowing the peer directly
-#endif
-#ifdef ZT_ENABLE_CLUSTER
- }
-#endif
+ return false;
}
- unsigned int chunkSize = std::min(packet.size(),(unsigned int)ZT_UDP_DEFAULT_PAYLOAD_MTU);
+ unsigned int mtu = ZT_DEFAULT_PHYSMTU;
+ uint64_t trustedPathId = 0;
+ RR->topology->getOutboundPathInfo(viaPath->address(),mtu,trustedPathId);
+
+ unsigned int chunkSize = std::min(packet.size(),mtu);
packet.setFragmented(chunkSize < packet.size());
-#ifdef ZT_ENABLE_CLUSTER
- const uint64_t trustedPathId = (viaPath) ? RR->topology->getOutboundPathTrust(viaPath->address()) : 0;
- if (trustedPathId) {
- packet.setTrusted(trustedPathId);
- } else {
- packet.armor((clusterMostRecentMemberId >= 0) ? clusterPeerSecret : peer->key(),encrypt,(viaPath) ? viaPath->nextOutgoingCounter() : 0);
- }
-#else
- const uint64_t trustedPathId = RR->topology->getOutboundPathTrust(viaPath->address());
if (trustedPathId) {
packet.setTrusted(trustedPathId);
} else {
- packet.armor(peer->key(),encrypt,viaPath->nextOutgoingCounter());
+ packet.armor(peer->key(),encrypt);
}
-#endif
-#ifdef ZT_ENABLE_CLUSTER
- if ( ((viaPath)&&(viaPath->send(RR,tPtr,packet.data(),chunkSize,now))) || ((clusterMostRecentMemberId >= 0)&&(RR->cluster->sendViaCluster(clusterMostRecentMemberId,destination,packet.data(),chunkSize))) ) {
-#else
if (viaPath->send(RR,tPtr,packet.data(),chunkSize,now)) {
-#endif
if (chunkSize < packet.size()) {
// Too big for one packet, fragment the rest
unsigned int fragStart = chunkSize;
unsigned int remaining = packet.size() - chunkSize;
- unsigned int fragsRemaining = (remaining / (ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH));
- if ((fragsRemaining * (ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH)) < remaining)
+ unsigned int fragsRemaining = (remaining / (mtu - ZT_PROTO_MIN_FRAGMENT_LENGTH));
+ if ((fragsRemaining * (mtu - ZT_PROTO_MIN_FRAGMENT_LENGTH)) < remaining)
++fragsRemaining;
const unsigned int totalFragments = fragsRemaining + 1;
for(unsigned int fno=1;fno<totalFragments;++fno) {
- chunkSize = std::min(remaining,(unsigned int)(ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH));
+ chunkSize = std::min(remaining,(unsigned int)(mtu - ZT_PROTO_MIN_FRAGMENT_LENGTH));
Packet::Fragment frag(packet,fragStart,chunkSize,fno,totalFragments);
-#ifdef ZT_ENABLE_CLUSTER
- if (viaPath)
- viaPath->send(RR,tPtr,frag.data(),frag.size(),now);
- else if (clusterMostRecentMemberId >= 0)
- RR->cluster->sendViaCluster(clusterMostRecentMemberId,destination,frag.data(),frag.size());
-#else
viaPath->send(RR,tPtr,frag.data(),frag.size(),now);
-#endif
fragStart += chunkSize;
remaining -= chunkSize;
}
diff --git a/node/Switch.hpp b/node/Switch.hpp
index ff350934..906f418e 100644
--- a/node/Switch.hpp
+++ b/node/Switch.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_N_SWITCH_HPP
@@ -27,12 +35,10 @@
#include "Constants.hpp"
#include "Mutex.hpp"
#include "MAC.hpp"
-#include "NonCopyable.hpp"
#include "Packet.hpp"
#include "Utils.hpp"
#include "InetAddress.hpp"
#include "Topology.hpp"
-#include "Array.hpp"
#include "Network.hpp"
#include "SharedPtr.hpp"
#include "IncomingPacket.hpp"
@@ -51,7 +57,7 @@ class Peer;
* packets from tap devices, and this sends them where they need to go and
* wraps/unwraps accordingly. It also handles queues and timeouts and such.
*/
-class Switch : NonCopyable
+class Switch
{
public:
Switch(const RuntimeEnvironment *renv);
@@ -60,12 +66,12 @@ public:
* Called when a packet is received from the real network
*
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
- * @param localAddr Local interface address
+ * @param localSocket Local I/O socket as supplied by external code
* @param fromAddr Internet IP address of origin
* @param data Packet data
* @param len Packet length
*/
- void onRemotePacket(void *tPtr,const InetAddress &localAddr,const InetAddress &fromAddr,const void *data,unsigned int len);
+ void onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddress &fromAddr,const void *data,unsigned int len);
/**
* Called when a packet comes from a local Ethernet tap
@@ -103,9 +109,10 @@ public:
* Request WHOIS on a given address
*
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param now Current time
* @param addr Address to look up
*/
- void requestWhois(void *tPtr,const Address &addr);
+ void requestWhois(void *tPtr,const int64_t now,const Address &addr);
/**
* Run any processes that are waiting for this peer's identity
@@ -127,59 +134,52 @@ public:
* @param now Current time
* @return Number of milliseconds until doTimerTasks() should be run again
*/
- unsigned long doTimerTasks(void *tPtr,uint64_t now);
+ unsigned long doTimerTasks(void *tPtr,int64_t now);
private:
- bool _shouldUnite(const uint64_t now,const Address &source,const Address &destination);
- Address _sendWhoisRequest(void *tPtr,const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted);
+ bool _shouldUnite(const int64_t now,const Address &source,const Address &destination);
bool _trySend(void *tPtr,Packet &packet,bool encrypt); // packet is modified if return is true
const RuntimeEnvironment *const RR;
- uint64_t _lastBeaconResponse;
+ int64_t _lastBeaconResponse;
+ volatile int64_t _lastCheckedQueues;
- // Outstanding WHOIS requests and how many retries they've undergone
- struct WhoisRequest
- {
- WhoisRequest() : lastSent(0),retries(0) {}
- uint64_t lastSent;
- Address peersConsulted[ZT_MAX_WHOIS_RETRIES]; // by retry
- unsigned int retries; // 0..ZT_MAX_WHOIS_RETRIES
- };
- Hashtable< Address,WhoisRequest > _outstandingWhoisRequests;
- Mutex _outstandingWhoisRequests_m;
+ // Time we last sent a WHOIS request for each address
+ Hashtable< Address,int64_t > _lastSentWhoisRequest;
+ Mutex _lastSentWhoisRequest_m;
// Packets waiting for WHOIS replies or other decode info or missing fragments
struct RXQueueEntry
{
RXQueueEntry() : timestamp(0) {}
- uint64_t timestamp; // 0 if entry is not in use
- uint64_t packetId;
+ volatile int64_t timestamp; // 0 if entry is not in use
+ volatile uint64_t packetId;
IncomingPacket frag0; // head of packet
Packet::Fragment frags[ZT_MAX_PACKET_FRAGMENTS - 1]; // later fragments (if any)
unsigned int totalFragments; // 0 if only frag0 received, waiting for frags
uint32_t haveFragments; // bit mask, LSB to MSB
- bool complete; // if true, packet is complete
+ volatile bool complete; // if true, packet is complete
};
RXQueueEntry _rxQueue[ZT_RX_QUEUE_SIZE];
- Mutex _rxQueue_m;
+ AtomicCounter _rxQueuePtr;
- /* Returns the matching or oldest entry. Caller must check timestamp and
- * packet ID to determine which. */
- inline RXQueueEntry *_findRXQueueEntry(uint64_t now,uint64_t packetId)
+ // Returns matching or next available RX queue entry
+ inline RXQueueEntry *_findRXQueueEntry(uint64_t packetId)
{
- RXQueueEntry *rq;
- RXQueueEntry *oldest = &(_rxQueue[ZT_RX_QUEUE_SIZE - 1]);
- unsigned long i = ZT_RX_QUEUE_SIZE;
- while (i) {
- rq = &(_rxQueue[--i]);
+ const unsigned int current = static_cast<unsigned int>(_rxQueuePtr.load());
+ for(unsigned int k=1;k<=ZT_RX_QUEUE_SIZE;++k) {
+ RXQueueEntry *rq = &(_rxQueue[(current - k) % ZT_RX_QUEUE_SIZE]);
if ((rq->packetId == packetId)&&(rq->timestamp))
return rq;
- if ((now - rq->timestamp) >= ZT_RX_QUEUE_EXPIRE)
- rq->timestamp = 0;
- if (rq->timestamp < oldest->timestamp)
- oldest = rq;
}
- return oldest;
+ ++_rxQueuePtr;
+ return &(_rxQueue[static_cast<unsigned int>(current) % ZT_RX_QUEUE_SIZE]);
+ }
+
+ // Returns current entry in rx queue ring buffer and increments ring pointer
+ inline RXQueueEntry *_nextRXQueueEntry()
+ {
+ return &(_rxQueue[static_cast<unsigned int>((++_rxQueuePtr) - 1) % ZT_RX_QUEUE_SIZE]);
}
// ZeroTier-layer TX queue entry
@@ -214,8 +214,8 @@ private:
y = a2.toInt();
}
}
- inline unsigned long hashCode() const throw() { return ((unsigned long)x ^ (unsigned long)y); }
- inline bool operator==(const _LastUniteKey &k) const throw() { return ((x == k.x)&&(y == k.y)); }
+ inline unsigned long hashCode() const { return ((unsigned long)x ^ (unsigned long)y); }
+ inline bool operator==(const _LastUniteKey &k) const { return ((x == k.x)&&(y == k.y)); }
uint64_t x,y;
};
Hashtable< _LastUniteKey,uint64_t > _lastUniteAttempt; // key is always sorted in ascending order, for set-like behavior
diff --git a/node/Tag.cpp b/node/Tag.cpp
index 3f924da1..62d9cb2e 100644
--- a/node/Tag.cpp
+++ b/node/Tag.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include "Tag.hpp"
@@ -22,6 +30,7 @@
#include "Topology.hpp"
#include "Switch.hpp"
#include "Network.hpp"
+#include "Node.hpp"
namespace ZeroTier {
@@ -31,7 +40,7 @@ int Tag::verify(const RuntimeEnvironment *RR,void *tPtr) const
return -1;
const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
if (!id) {
- RR->sw->requestWhois(tPtr,_signedBy);
+ RR->sw->requestWhois(tPtr,RR->node->now(),_signedBy);
return 1;
}
try {
diff --git a/node/Tag.hpp b/node/Tag.hpp
index 1f7f6835..d2e932c2 100644
--- a/node/Tag.hpp
+++ b/node/Tag.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_TAG_HPP
@@ -69,7 +77,7 @@ public:
* @param id Tag ID
* @param value Tag value
*/
- Tag(const uint64_t nwid,const uint64_t ts,const Address &issuedTo,const uint32_t id,const uint32_t value) :
+ Tag(const uint64_t nwid,const int64_t ts,const Address &issuedTo,const uint32_t id,const uint32_t value) :
_id(id),
_value(value),
_networkId(nwid),
@@ -82,7 +90,7 @@ public:
inline uint32_t id() const { return _id; }
inline const uint32_t &value() const { return _value; }
inline uint64_t networkId() const { return _networkId; }
- inline uint64_t timestamp() const { return _ts; }
+ inline int64_t timestamp() const { return _ts; }
inline const Address &issuedTo() const { return _issuedTo; }
inline const Address &signedBy() const { return _signedBy; }
@@ -153,16 +161,16 @@ public:
_signedBy.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
if (b[p++] == 1) {
if (b.template at<uint16_t>(p) != ZT_C25519_SIGNATURE_LEN)
- throw std::runtime_error("invalid signature length");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
p += 2;
- memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
+ ZT_FAST_MEMCPY(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
} else {
p += 2 + b.template at<uint16_t>(p);
}
p += 2 + b.template at<uint16_t>(p);
if (p > b.size())
- throw std::runtime_error("extended field overflow");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
return (p - startAt);
}
@@ -191,7 +199,7 @@ private:
uint32_t _id;
uint32_t _value;
uint64_t _networkId;
- uint64_t _ts;
+ int64_t _ts;
Address _issuedTo;
Address _signedBy;
C25519::Signature _signature;
diff --git a/node/Topology.cpp b/node/Topology.cpp
index a1d37332..7c526b41 100644
--- a/node/Topology.cpp
+++ b/node/Topology.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include "Constants.hpp"
@@ -57,18 +65,20 @@ static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x0
Topology::Topology(const RuntimeEnvironment *renv,void *tPtr) :
RR(renv),
- _trustedPathCount(0),
- _amRoot(false)
+ _numConfiguredPhysicalPaths(0),
+ _amUpstream(false)
{
- try {
- World cachedPlanet;
- std::string buf(RR->node->dataStoreGet(tPtr,"planet"));
- if (buf.length() > 0) {
- Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> dswtmp(buf.data(),(unsigned int)buf.length());
- cachedPlanet.deserialize(dswtmp,0);
- }
- addWorld(tPtr,cachedPlanet,false);
- } catch ( ... ) {}
+ uint8_t tmp[ZT_WORLD_MAX_SERIALIZED_LENGTH];
+ uint64_t idtmp[2];
+ idtmp[0] = 0; idtmp[1] = 0;
+ int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_PLANET,idtmp,tmp,sizeof(tmp));
+ if (n > 0) {
+ try {
+ World cachedPlanet;
+ cachedPlanet.deserialize(Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH>(tmp,(unsigned int)n),0);
+ addWorld(tPtr,cachedPlanet,false);
+ } catch ( ... ) {} // ignore invalid cached planets
+ }
World defaultPlanet;
{
@@ -78,17 +88,17 @@ Topology::Topology(const RuntimeEnvironment *renv,void *tPtr) :
addWorld(tPtr,defaultPlanet,false);
}
-SharedPtr<Peer> Topology::addPeer(void *tPtr,const SharedPtr<Peer> &peer)
+Topology::~Topology()
{
-#ifdef ZT_TRACE
- if ((!peer)||(peer->address() == RR->identity.address())) {
- if (!peer)
- fprintf(stderr,"FATAL BUG: addPeer() caught attempt to add NULL peer" ZT_EOL_S);
- else fprintf(stderr,"FATAL BUG: addPeer() caught attempt to add peer for self" ZT_EOL_S);
- abort();
- }
-#endif
+ Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
+ Address *a = (Address *)0;
+ SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
+ while (i.next(a,p))
+ _savePeer((void *)0,*p);
+}
+SharedPtr<Peer> Topology::addPeer(void *tPtr,const SharedPtr<Peer> &peer)
+{
SharedPtr<Peer> np;
{
Mutex::Lock _l(_peers_m);
@@ -97,18 +107,13 @@ SharedPtr<Peer> Topology::addPeer(void *tPtr,const SharedPtr<Peer> &peer)
hp = peer;
np = hp;
}
-
- saveIdentity(tPtr,np->identity());
-
return np;
}
SharedPtr<Peer> Topology::getPeer(void *tPtr,const Address &zta)
{
- if (zta == RR->identity.address()) {
- TRACE("BUG: ignored attempt to getPeer() for self, returned NULL");
+ if (zta == RR->identity.address())
return SharedPtr<Peer>();
- }
{
Mutex::Lock _l(_peers_m);
@@ -118,18 +123,22 @@ SharedPtr<Peer> Topology::getPeer(void *tPtr,const Address &zta)
}
try {
- Identity id(_getIdentity(tPtr,zta));
- if (id) {
- SharedPtr<Peer> np(new Peer(RR,RR->identity,id));
- {
- Mutex::Lock _l(_peers_m);
- SharedPtr<Peer> &ap = _peers[zta];
- if (!ap)
- ap.swap(np);
+ Buffer<ZT_PEER_MAX_SERIALIZED_STATE_SIZE> buf;
+ uint64_t idbuf[2]; idbuf[0] = zta.toInt(); idbuf[1] = 0;
+ int len = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_PEER,idbuf,buf.unsafeData(),ZT_PEER_MAX_SERIALIZED_STATE_SIZE);
+ if (len > 0) {
+ buf.setSize(len);
+ Mutex::Lock _l(_peers_m);
+ SharedPtr<Peer> &ap = _peers[zta];
+ if (ap)
return ap;
+ ap = Peer::deserializeFromCache(RR->node->now(),tPtr,buf,RR);
+ if (!ap) {
+ _peers.erase(zta);
}
+ return SharedPtr<Peer>();
}
- } catch ( ... ) {} // invalid identity on disk?
+ } catch ( ... ) {} // ignore invalid identities or other strage failures
return SharedPtr<Peer>();
}
@@ -144,58 +153,32 @@ Identity Topology::getIdentity(void *tPtr,const Address &zta)
if (ap)
return (*ap)->identity();
}
- return _getIdentity(tPtr,zta);
-}
-
-void Topology::saveIdentity(void *tPtr,const Identity &id)
-{
- if (id) {
- char p[128];
- Utils::snprintf(p,sizeof(p),"iddb.d/%.10llx",(unsigned long long)id.address().toInt());
- RR->node->dataStorePut(tPtr,p,id.toString(false),false);
- }
+ return Identity();
}
-SharedPtr<Peer> Topology::getUpstreamPeer(const Address *avoid,unsigned int avoidCount,bool strictAvoid)
+SharedPtr<Peer> Topology::getUpstreamPeer()
{
- const uint64_t now = RR->node->now();
- unsigned int bestQualityOverall = ~((unsigned int)0);
- unsigned int bestQualityNotAvoid = ~((unsigned int)0);
- const SharedPtr<Peer> *bestOverall = (const SharedPtr<Peer> *)0;
- const SharedPtr<Peer> *bestNotAvoid = (const SharedPtr<Peer> *)0;
+ const int64_t now = RR->node->now();
+ unsigned int bestq = ~((unsigned int)0);
+ const SharedPtr<Peer> *best = (const SharedPtr<Peer> *)0;
- Mutex::Lock _l1(_peers_m);
- Mutex::Lock _l2(_upstreams_m);
+ Mutex::Lock _l2(_peers_m);
+ Mutex::Lock _l1(_upstreams_m);
for(std::vector<Address>::const_iterator a(_upstreamAddresses.begin());a!=_upstreamAddresses.end();++a) {
const SharedPtr<Peer> *p = _peers.get(*a);
if (p) {
- bool avoiding = false;
- for(unsigned int i=0;i<avoidCount;++i) {
- if (avoid[i] == (*p)->address()) {
- avoiding = true;
- break;
- }
- }
const unsigned int q = (*p)->relayQuality(now);
- if (q <= bestQualityOverall) {
- bestQualityOverall = q;
- bestOverall = &(*p);
- }
- if ((!avoiding)&&(q <= bestQualityNotAvoid)) {
- bestQualityNotAvoid = q;
- bestNotAvoid = &(*p);
+ if (q <= bestq) {
+ bestq = q;
+ best = p;
}
}
}
- if (bestNotAvoid) {
- return *bestNotAvoid;
- } else if ((!strictAvoid)&&(bestOverall)) {
- return *bestOverall;
- }
-
- return SharedPtr<Peer>();
+ if (!best)
+ return SharedPtr<Peer>();
+ return *best;
}
bool Topology::isUpstream(const Identity &id) const
@@ -269,8 +252,8 @@ bool Topology::addWorld(void *tPtr,const World &newWorld,bool alwaysAcceptNew)
if ((newWorld.type() != World::TYPE_PLANET)&&(newWorld.type() != World::TYPE_MOON))
return false;
- Mutex::Lock _l1(_upstreams_m);
Mutex::Lock _l2(_peers_m);
+ Mutex::Lock _l1(_upstreams_m);
World *existing = (World *)0;
switch(newWorld.type()) {
@@ -319,19 +302,13 @@ bool Topology::addWorld(void *tPtr,const World &newWorld,bool alwaysAcceptNew)
return false;
}
- char savePath[64];
- if (existing->type() == World::TYPE_MOON) {
- Utils::snprintf(savePath,sizeof(savePath),"moons.d/%.16llx.moon",existing->id());
- } else {
- Utils::scopy(savePath,sizeof(savePath),"planet");
- }
try {
- Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> dswtmp;
- existing->serialize(dswtmp,false);
- RR->node->dataStorePut(tPtr,savePath,dswtmp.data(),dswtmp.size(),false);
- } catch ( ... ) {
- RR->node->dataStoreDelete(tPtr,savePath);
- }
+ Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> sbuf;
+ existing->serialize(sbuf,false);
+ uint64_t idtmp[2];
+ idtmp[0] = existing->id(); idtmp[1] = 0;
+ RR->node->stateObjectPut(tPtr,(existing->type() == World::TYPE_PLANET) ? ZT_STATE_OBJECT_PLANET : ZT_STATE_OBJECT_MOON,idtmp,sbuf.data(),sbuf.size());
+ } catch ( ... ) {}
_memoizeUpstreams(tPtr);
@@ -340,21 +317,20 @@ bool Topology::addWorld(void *tPtr,const World &newWorld,bool alwaysAcceptNew)
void Topology::addMoon(void *tPtr,const uint64_t id,const Address &seed)
{
- char savePath[64];
- Utils::snprintf(savePath,sizeof(savePath),"moons.d/%.16llx.moon",id);
-
- try {
- std::string moonBin(RR->node->dataStoreGet(tPtr,savePath));
- if (moonBin.length() > 1) {
- Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> wtmp(moonBin.data(),(unsigned int)moonBin.length());
+ char tmp[ZT_WORLD_MAX_SERIALIZED_LENGTH];
+ uint64_t idtmp[2];
+ idtmp[0] = id; idtmp[1] = 0;
+ int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_MOON,idtmp,tmp,sizeof(tmp));
+ if (n > 0) {
+ try {
World w;
- w.deserialize(wtmp);
+ w.deserialize(Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH>(tmp,(unsigned int)n));
if ((w.type() == World::TYPE_MOON)&&(w.id() == id)) {
addWorld(tPtr,w,true);
return;
}
- }
- } catch ( ... ) {}
+ } catch ( ... ) {}
+ }
if (seed) {
Mutex::Lock _l(_upstreams_m);
@@ -365,17 +341,17 @@ void Topology::addMoon(void *tPtr,const uint64_t id,const Address &seed)
void Topology::removeMoon(void *tPtr,const uint64_t id)
{
- Mutex::Lock _l1(_upstreams_m);
Mutex::Lock _l2(_peers_m);
+ Mutex::Lock _l1(_upstreams_m);
std::vector<World> nm;
for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
if (m->id() != id) {
nm.push_back(*m);
} else {
- char savePath[64];
- Utils::snprintf(savePath,sizeof(savePath),"moons.d/%.16llx.moon",id);
- RR->node->dataStoreDelete(tPtr,savePath);
+ uint64_t idtmp[2];
+ idtmp[0] = id; idtmp[1] = 0;
+ RR->node->stateObjectDelete(tPtr,ZT_STATE_OBJECT_MOON,idtmp);
}
}
_moons.swap(nm);
@@ -390,7 +366,7 @@ void Topology::removeMoon(void *tPtr,const uint64_t id)
_memoizeUpstreams(tPtr);
}
-void Topology::clean(uint64_t now)
+void Topology::doPeriodicTasks(void *tPtr,int64_t now)
{
{
Mutex::Lock _l1(_peers_m);
@@ -399,77 +375,66 @@ void Topology::clean(uint64_t now)
Address *a = (Address *)0;
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
while (i.next(a,p)) {
- if ( (!(*p)->isAlive(now)) && (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),*a) == _upstreamAddresses.end()) )
+ if ( (!(*p)->isAlive(now)) && (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),*a) == _upstreamAddresses.end()) ) {
+ _savePeer(tPtr,*p);
_peers.erase(*a);
+ }
}
}
+
{
Mutex::Lock _l(_paths_m);
Hashtable< Path::HashKey,SharedPtr<Path> >::Iterator i(_paths);
Path::HashKey *k = (Path::HashKey *)0;
SharedPtr<Path> *p = (SharedPtr<Path> *)0;
while (i.next(k,p)) {
- if (p->reclaimIfWeak())
+ if (p->references() <= 1)
_paths.erase(*k);
}
}
}
-Identity Topology::_getIdentity(void *tPtr,const Address &zta)
-{
- char p[128];
- Utils::snprintf(p,sizeof(p),"iddb.d/%.10llx",(unsigned long long)zta.toInt());
- std::string ids(RR->node->dataStoreGet(tPtr,p));
- if (ids.length() > 0) {
- try {
- return Identity(ids);
- } catch ( ... ) {} // ignore invalid IDs
- }
- return Identity();
-}
-
void Topology::_memoizeUpstreams(void *tPtr)
{
// assumes _upstreams_m and _peers_m are locked
_upstreamAddresses.clear();
- _amRoot = false;
+ _amUpstream = false;
for(std::vector<World::Root>::const_iterator i(_planet.roots().begin());i!=_planet.roots().end();++i) {
if (i->identity == RR->identity) {
- _amRoot = true;
+ _amUpstream = true;
} else if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),i->identity.address()) == _upstreamAddresses.end()) {
_upstreamAddresses.push_back(i->identity.address());
SharedPtr<Peer> &hp = _peers[i->identity.address()];
- if (!hp) {
+ if (!hp)
hp = new Peer(RR,RR->identity,i->identity);
- saveIdentity(tPtr,i->identity);
- }
}
}
for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
for(std::vector<World::Root>::const_iterator i(m->roots().begin());i!=m->roots().end();++i) {
if (i->identity == RR->identity) {
- _amRoot = true;
+ _amUpstream = true;
} else if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),i->identity.address()) == _upstreamAddresses.end()) {
_upstreamAddresses.push_back(i->identity.address());
SharedPtr<Peer> &hp = _peers[i->identity.address()];
- if (!hp) {
+ if (!hp)
hp = new Peer(RR,RR->identity,i->identity);
- saveIdentity(tPtr,i->identity);
- }
}
}
}
std::sort(_upstreamAddresses.begin(),_upstreamAddresses.end());
+}
- _cor.clear();
- for(std::vector<Address>::const_iterator a(_upstreamAddresses.begin());a!=_upstreamAddresses.end();++a) {
- if (!_cor.addRepresentative(*a))
- break;
- }
- _cor.sign(RR->identity,RR->node->now());
+void Topology::_savePeer(void *tPtr,const SharedPtr<Peer> &peer)
+{
+ try {
+ Buffer<ZT_PEER_MAX_SERIALIZED_STATE_SIZE> buf;
+ peer->serializeForCache(buf);
+ uint64_t tmpid[2]; tmpid[0] = peer->address().toInt(); tmpid[1] = 0;
+ RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_PEER,tmpid,buf.data(),buf.size());
+ } catch ( ... ) {} // sanity check, discard invalid entries
}
} // namespace ZeroTier
diff --git a/node/Topology.hpp b/node/Topology.hpp
index d29c424e..63946a32 100644
--- a/node/Topology.hpp
+++ b/node/Topology.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_TOPOLOGY_HPP
@@ -38,7 +46,6 @@
#include "InetAddress.hpp"
#include "Hashtable.hpp"
#include "World.hpp"
-#include "CertificateOfRepresentation.hpp"
namespace ZeroTier {
@@ -51,6 +58,7 @@ class Topology
{
public:
Topology(const RuntimeEnvironment *renv,void *tPtr);
+ ~Topology();
/**
* Add a peer to database
@@ -74,6 +82,13 @@ public:
SharedPtr<Peer> getPeer(void *tPtr,const Address &zta);
/**
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param zta ZeroTier address of peer
+ * @return Identity or NULL identity if not found
+ */
+ Identity getIdentity(void *tPtr,const Address &zta);
+
+ /**
* Get a peer only if it is presently in memory (no disk cache)
*
* This also does not update the lastUsed() time for peers, which means
@@ -95,55 +110,25 @@ public:
/**
* Get a Path object for a given local and remote physical address, creating if needed
*
- * @param l Local address or NULL for 'any' or 'wildcard'
+ * @param l Local socket
* @param r Remote address
* @return Pointer to canonicalized Path object
*/
- inline SharedPtr<Path> getPath(const InetAddress &l,const InetAddress &r)
+ inline SharedPtr<Path> getPath(const int64_t l,const InetAddress &r)
{
Mutex::Lock _l(_paths_m);
SharedPtr<Path> &p = _paths[Path::HashKey(l,r)];
if (!p)
- p.setToUnsafe(new Path(l,r));
+ p.set(new Path(l,r));
return p;
}
/**
- * Get the identity of a peer
- *
- * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
- * @param zta ZeroTier address of peer
- * @return Identity or NULL Identity if not found
- */
- Identity getIdentity(void *tPtr,const Address &zta);
-
- /**
- * Cache an identity
- *
- * This is done automatically on addPeer(), and so is only useful for
- * cluster identity replication.
- *
- * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
- * @param id Identity to cache
- */
- void saveIdentity(void *tPtr,const Identity &id);
-
- /**
* Get the current best upstream peer
*
- * @return Root server with lowest latency or NULL if none
+ * @return Upstream or NULL if none available
*/
- inline SharedPtr<Peer> getUpstreamPeer() { return getUpstreamPeer((const Address *)0,0,false); }
-
- /**
- * Get the current best upstream peer, avoiding those in the supplied avoid list
- *
- * @param avoid Nodes to avoid
- * @param avoidCount Number of nodes to avoid
- * @param strictAvoid If false, consider avoided root servers anyway if no non-avoid root servers are available
- * @return Root server or NULL if none available
- */
- SharedPtr<Peer> getUpstreamPeer(const Address *avoid,unsigned int avoidCount,bool strictAvoid);
+ SharedPtr<Peer> getUpstreamPeer();
/**
* @param id Identity to check
@@ -300,13 +285,13 @@ public:
/**
* Clean and flush database
*/
- void clean(uint64_t now);
+ void doPeriodicTasks(void *tPtr,int64_t now);
/**
* @param now Current time
* @return Number of peers with active direct paths
*/
- inline unsigned long countActive(uint64_t now) const
+ inline unsigned long countActive(int64_t now) const
{
unsigned long cnt = 0;
Mutex::Lock _l(_peers_m);
@@ -315,7 +300,7 @@ public:
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
while (i.next(a,p)) {
const SharedPtr<Path> pp((*p)->getBestPath(now,false));
- if ((pp)&&(pp->alive(now)))
+ if (pp)
++cnt;
}
return cnt;
@@ -335,12 +320,6 @@ public:
Address *a = (Address *)0;
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
while (i.next(a,p)) {
-#ifdef ZT_TRACE
- if (!(*p)) {
- fprintf(stderr,"FATAL BUG: eachPeer() caught NULL peer for %s -- peer pointers in Topology should NEVER be NULL" ZT_EOL_S,a->toString().c_str());
- abort();
- }
-#endif
f(*this,*((const SharedPtr<Peer> *)p));
}
}
@@ -357,86 +336,119 @@ public:
/**
* @return True if I am a root server in a planet or moon
*/
- inline bool amRoot() const { return _amRoot; }
+ inline bool amUpstream() const { return _amUpstream; }
/**
- * Get the outbound trusted path ID for a physical address, or 0 if none
+ * Get info about a path
*
- * @param physicalAddress Physical address to which we are sending the packet
- * @return Trusted path ID or 0 if none (0 is not a valid trusted path ID)
+ * The supplied result variables are not modified if no special config info is found.
+ *
+ * @param physicalAddress Physical endpoint address
+ * @param mtu Variable set to MTU
+ * @param trustedPathId Variable set to trusted path ID
*/
- inline uint64_t getOutboundPathTrust(const InetAddress &physicalAddress)
+ inline void getOutboundPathInfo(const InetAddress &physicalAddress,unsigned int &mtu,uint64_t &trustedPathId)
{
- for(unsigned int i=0;i<_trustedPathCount;++i) {
- if (_trustedPathNetworks[i].containsAddress(physicalAddress))
- return _trustedPathIds[i];
+ for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i<j;++i) {
+ if (_physicalPathConfig[i].first.containsAddress(physicalAddress)) {
+ trustedPathId = _physicalPathConfig[i].second.trustedPathId;
+ mtu = _physicalPathConfig[i].second.mtu;
+ return;
+ }
}
- return 0;
}
/**
- * Check whether in incoming trusted path marked packet is valid
+ * Get the payload MTU for an outbound physical path (returns default if not configured)
*
- * @param physicalAddress Originating physical address
- * @param trustedPathId Trusted path ID from packet (from MAC field)
+ * @param physicalAddress Physical endpoint address
+ * @return MTU
*/
- inline bool shouldInboundPathBeTrusted(const InetAddress &physicalAddress,const uint64_t trustedPathId)
+ inline unsigned int getOutboundPathMtu(const InetAddress &physicalAddress)
{
- for(unsigned int i=0;i<_trustedPathCount;++i) {
- if ((_trustedPathIds[i] == trustedPathId)&&(_trustedPathNetworks[i].containsAddress(physicalAddress)))
- return true;
+ for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i<j;++i) {
+ if (_physicalPathConfig[i].first.containsAddress(physicalAddress))
+ return _physicalPathConfig[i].second.mtu;
}
- return false;
+ return ZT_DEFAULT_PHYSMTU;
}
/**
- * Set trusted paths in this topology
+ * Get the outbound trusted path ID for a physical address, or 0 if none
*
- * @param networks Array of networks (prefix/netmask bits)
- * @param ids Array of trusted path IDs
- * @param count Number of trusted paths (if larger than ZT_MAX_TRUSTED_PATHS overflow is ignored)
+ * @param physicalAddress Physical address to which we are sending the packet
+ * @return Trusted path ID or 0 if none (0 is not a valid trusted path ID)
*/
- inline void setTrustedPaths(const InetAddress *networks,const uint64_t *ids,unsigned int count)
+ inline uint64_t getOutboundPathTrust(const InetAddress &physicalAddress)
{
- if (count > ZT_MAX_TRUSTED_PATHS)
- count = ZT_MAX_TRUSTED_PATHS;
- Mutex::Lock _l(_trustedPaths_m);
- for(unsigned int i=0;i<count;++i) {
- _trustedPathIds[i] = ids[i];
- _trustedPathNetworks[i] = networks[i];
+ for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i<j;++i) {
+ if (_physicalPathConfig[i].first.containsAddress(physicalAddress))
+ return _physicalPathConfig[i].second.trustedPathId;
}
- _trustedPathCount = count;
+ return 0;
}
/**
- * @return Current certificate of representation (copy)
+ * Check whether in incoming trusted path marked packet is valid
+ *
+ * @param physicalAddress Originating physical address
+ * @param trustedPathId Trusted path ID from packet (from MAC field)
*/
- inline CertificateOfRepresentation certificateOfRepresentation() const
+ inline bool shouldInboundPathBeTrusted(const InetAddress &physicalAddress,const uint64_t trustedPathId)
{
- Mutex::Lock _l(_upstreams_m);
- return _cor;
+ for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i<j;++i) {
+ if ((_physicalPathConfig[i].second.trustedPathId == trustedPathId)&&(_physicalPathConfig[i].first.containsAddress(physicalAddress)))
+ return true;
+ }
+ return false;
}
/**
- * @param buf Buffer to receive COR
+ * Set or clear physical path configuration (called via Node::setPhysicalPathConfiguration)
*/
- template<unsigned int C>
- void appendCertificateOfRepresentation(Buffer<C> &buf)
+ inline void setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig)
{
- Mutex::Lock _l(_upstreams_m);
- _cor.serialize(buf);
+ if (!pathNetwork) {
+ _numConfiguredPhysicalPaths = 0;
+ } else {
+ std::map<InetAddress,ZT_PhysicalPathConfiguration> cpaths;
+ for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i<j;++i)
+ cpaths[_physicalPathConfig[i].first] = _physicalPathConfig[i].second;
+
+ if (pathConfig) {
+ ZT_PhysicalPathConfiguration pc(*pathConfig);
+
+ if (pc.mtu <= 0)
+ pc.mtu = ZT_DEFAULT_PHYSMTU;
+ else if (pc.mtu < ZT_MIN_PHYSMTU)
+ pc.mtu = ZT_MIN_PHYSMTU;
+ else if (pc.mtu > ZT_MAX_PHYSMTU)
+ pc.mtu = ZT_MAX_PHYSMTU;
+
+ cpaths[*(reinterpret_cast<const InetAddress *>(pathNetwork))] = pc;
+ } else {
+ cpaths.erase(*(reinterpret_cast<const InetAddress *>(pathNetwork)));
+ }
+
+ unsigned int cnt = 0;
+ for(std::map<InetAddress,ZT_PhysicalPathConfiguration>::const_iterator i(cpaths.begin());((i!=cpaths.end())&&(cnt<ZT_MAX_CONFIGURABLE_PATHS));++i) {
+ _physicalPathConfig[cnt].first = i->first;
+ _physicalPathConfig[cnt].second = i->second;
+ ++cnt;
+ }
+ _numConfiguredPhysicalPaths = cnt;
+ }
}
private:
Identity _getIdentity(void *tPtr,const Address &zta);
void _memoizeUpstreams(void *tPtr);
+ void _savePeer(void *tPtr,const SharedPtr<Peer> &peer);
const RuntimeEnvironment *const RR;
- uint64_t _trustedPathIds[ZT_MAX_TRUSTED_PATHS];
- InetAddress _trustedPathNetworks[ZT_MAX_TRUSTED_PATHS];
- unsigned int _trustedPathCount;
- Mutex _trustedPaths_m;
+ std::pair<InetAddress,ZT_PhysicalPathConfiguration> _physicalPathConfig[ZT_MAX_CONFIGURABLE_PATHS];
+ volatile unsigned int _numConfiguredPhysicalPaths;
Hashtable< Address,SharedPtr<Peer> > _peers;
Mutex _peers_m;
@@ -448,8 +460,7 @@ private:
std::vector<World> _moons;
std::vector< std::pair<uint64_t,Address> > _moonSeeds;
std::vector<Address> _upstreamAddresses;
- CertificateOfRepresentation _cor;
- bool _amRoot;
+ bool _amUpstream;
Mutex _upstreams_m; // locks worlds, upstream info, moon info, etc.
};
diff --git a/node/Trace.cpp b/node/Trace.cpp
new file mode 100644
index 00000000..386edaac
--- /dev/null
+++ b/node/Trace.cpp
@@ -0,0 +1,540 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+//#define ZT_TRACE
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Trace.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Switch.hpp"
+#include "Node.hpp"
+#include "Utils.hpp"
+#include "Dictionary.hpp"
+#include "CertificateOfMembership.hpp"
+#include "CertificateOfOwnership.hpp"
+#include "Tag.hpp"
+#include "Capability.hpp"
+#include "Revocation.hpp"
+
+namespace ZeroTier {
+
+#ifdef ZT_TRACE
+static void ZT_LOCAL_TRACE(void *const tPtr,const RuntimeEnvironment *const RR,const char *const fmt,...)
+{
+ char traceMsgBuf[1024];
+ va_list ap;
+ va_start(ap,fmt);
+ vsnprintf(traceMsgBuf,sizeof(traceMsgBuf),fmt,ap);
+ va_end(ap);
+ traceMsgBuf[sizeof(traceMsgBuf) - 1] = (char)0;
+ RR->node->postEvent(tPtr,ZT_EVENT_TRACE,traceMsgBuf);
+}
+#else
+#define ZT_LOCAL_TRACE(...)
+#endif
+
+void Trace::resettingPathsInScope(void *const tPtr,const Address &reporter,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,const InetAddress::IpScope scope)
+{
+ char tmp[128];
+
+ ZT_LOCAL_TRACE(tPtr,RR,"RESET and revalidate paths in scope %d; new phy address %s reported by trusted peer %.10llx",(int)scope,myPhysicalAddress.toIpString(tmp),reporter.toInt());
+
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__RESETTING_PATHS_IN_SCOPE_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,reporter);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,reporterPhysicalAddress.toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_PHYADDR,myPhysicalAddress.toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__IP_SCOPE,(uint64_t)scope);
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ _spamToAllNetworks(tPtr,d,Trace::LEVEL_NORMAL);
+}
+
+void Trace::peerConfirmingUnknownPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &path,const uint64_t packetId,const Packet::Verb verb)
+{
+ char tmp[128];
+ if (!path) return; // sanity check
+
+ ZT_LOCAL_TRACE(tPtr,RR,"trying unknown path %s to %.10llx (packet %.16llx verb %d local socket %lld network %.16llx)",path->address().toString(tmp),peer.address().toInt(),packetId,(double)verb,path->localSocket(),networkId);
+
+ std::pair<Address,Trace::Level> byn;
+ if (networkId) { Mutex::Lock l(_byNet_m); _byNet.get(networkId,byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PEER_CONFIRMING_UNKNOWN_PATH_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_VERB,(uint64_t)verb);
+ if (networkId)
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,networkId);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,peer.address());
+ if (path) {
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
+ }
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::peerLearnedNewPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &newPath,const uint64_t packetId)
+{
+ char tmp[128];
+ if (!newPath) return; // sanity check
+
+ ZT_LOCAL_TRACE(tPtr,RR,"learned new path %s to %.10llx (packet %.16llx local socket %lld network %.16llx)",newPath->address().toString(tmp),peer.address().toInt(),packetId,newPath->localSocket(),networkId);
+
+ std::pair<Address,Trace::Level> byn;
+ if (networkId) { Mutex::Lock l(_byNet_m); _byNet.get(networkId,byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PEER_LEARNED_NEW_PATH_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
+ if (networkId)
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,networkId);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,peer.address());
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,newPath->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,newPath->localSocket());
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::peerRedirected(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &newPath)
+{
+ char tmp[128];
+ if (!newPath) return; // sanity check
+
+ ZT_LOCAL_TRACE(tPtr,RR,"explicit redirect from %.10llx to path %s",peer.address().toInt(),newPath->address().toString(tmp));
+
+ std::pair<Address,Trace::Level> byn;
+ if (networkId) { Mutex::Lock l(_byNet_m); _byNet.get(networkId,byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PEER_REDIRECTED_S);
+ if (networkId)
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,networkId);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,peer.address());
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,newPath->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,newPath->localSocket());
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::outgoingNetworkFrameDropped(void *const tPtr,const SharedPtr<Network> &network,const MAC &sourceMac,const MAC &destMac,const unsigned int etherType,const unsigned int vlanId,const unsigned int frameLen,const char *reason)
+{
+#ifdef ZT_TRACE
+ char tmp[128],tmp2[128];
+#endif
+ if (!network) return; // sanity check
+
+ ZT_LOCAL_TRACE(tPtr,RR,"%.16llx DROP frame %s -> %s etherType %.4x size %u (%s)",network->id(),sourceMac.toString(tmp),destMac.toString(tmp2),etherType,frameLen,(reason) ? reason : "unknown reason");
+
+ std::pair<Address,Trace::Level> byn;
+ { Mutex::Lock l(_byNet_m); _byNet.get(network->id(),byn); }
+
+ if ( ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE)) || ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE)) ) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__OUTGOING_NETWORK_FRAME_DROPPED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network->id());
+ d.add(ZT_REMOTE_TRACE_FIELD__SOURCE_MAC,sourceMac.toInt());
+ d.add(ZT_REMOTE_TRACE_FIELD__DEST_MAC,destMac.toInt());
+ d.add(ZT_REMOTE_TRACE_FIELD__ETHERTYPE,(uint64_t)etherType);
+ d.add(ZT_REMOTE_TRACE_FIELD__VLAN_ID,(uint64_t)vlanId);
+ d.add(ZT_REMOTE_TRACE_FIELD__FRAME_LENGTH,(uint64_t)frameLen);
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ if ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE))
+ _send(tPtr,d,_globalTarget);
+ if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE))
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::incomingNetworkAccessDenied(void *const tPtr,const SharedPtr<Network> &network,const SharedPtr<Path> &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,bool credentialsRequested)
+{
+ char tmp[128];
+ if (!network) return; // sanity check
+
+ ZT_LOCAL_TRACE(tPtr,RR,"%.16llx DENIED packet from %.10llx(%s) verb %d size %u%s",network->id(),source.toInt(),(path) ? (path->address().toString(tmp)) : "???",(int)verb,packetLength,credentialsRequested ? " (credentials requested)" : " (credentials not requested)");
+
+ std::pair<Address,Trace::Level> byn;
+ { Mutex::Lock l(_byNet_m); _byNet.get(network->id(),byn); }
+
+ if ( ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE)) || ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE)) ) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_ACCESS_DENIED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_VERB,(uint64_t)verb);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source);
+ if (path) {
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
+ }
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network->id());
+
+ if ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE))
+ _send(tPtr,d,_globalTarget);
+ if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE))
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::incomingNetworkFrameDropped(void *const tPtr,const SharedPtr<Network> &network,const SharedPtr<Path> &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,const MAC &sourceMac,const MAC &destMac,const char *reason)
+{
+ char tmp[128];
+ if (!network) return; // sanity check
+
+ ZT_LOCAL_TRACE(tPtr,RR,"%.16llx DROPPED frame from %.10llx(%s) verb %d size %u",network->id(),source.toInt(),(path) ? (path->address().toString(tmp)) : "???",(int)verb,packetLength);
+
+ std::pair<Address,Trace::Level> byn;
+ { Mutex::Lock l(_byNet_m); _byNet.get(network->id(),byn); }
+
+ if ( ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE)) || ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE)) ) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_FRAME_DROPPED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_VERB,(uint64_t)verb);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source);
+ if (path) {
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
+ }
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network->id());
+ d.add(ZT_REMOTE_TRACE_FIELD__SOURCE_MAC,sourceMac.toInt());
+ d.add(ZT_REMOTE_TRACE_FIELD__DEST_MAC,destMac.toInt());
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ if ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE))
+ _send(tPtr,d,_globalTarget);
+ if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE))
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::incomingPacketMessageAuthenticationFailure(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const unsigned int hops,const char *reason)
+{
+ char tmp[128];
+
+ ZT_LOCAL_TRACE(tPtr,RR,"MAC failed for packet %.16llx from %.10llx(%s)",packetId,source.toInt(),(path) ? path->address().toString(tmp) : "???");
+
+ if ((_globalTarget)&&((int)_globalLevel >= Trace::LEVEL_DEBUG)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PACKET_MAC_FAILURE_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_HOPS,(uint64_t)hops);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source);
+ if (path) {
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
+ }
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ _send(tPtr,d,_globalTarget);
+ }
+}
+
+void Trace::incomingPacketInvalid(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const unsigned int hops,const Packet::Verb verb,const char *reason)
+{
+ char tmp[128];
+
+ ZT_LOCAL_TRACE(tPtr,RR,"INVALID packet %.16llx from %.10llx(%s) (%s)",packetId,source.toInt(),(path) ? path->address().toString(tmp) : "???",(reason) ? reason : "unknown reason");
+
+ if ((_globalTarget)&&((int)_globalLevel >= Trace::LEVEL_DEBUG)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PACKET_INVALID_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_VERB,(uint64_t)verb);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source);
+ if (path) {
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
+ }
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_HOPS,(uint64_t)hops);
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ _send(tPtr,d,_globalTarget);
+ }
+}
+
+void Trace::incomingPacketDroppedHELLO(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const char *reason)
+{
+ char tmp[128];
+
+ ZT_LOCAL_TRACE(tPtr,RR,"DROPPED HELLO from %.10llx(%s) (%s)",source.toInt(),(path) ? path->address().toString(tmp) : "???",(reason) ? reason : "???");
+
+ if ((_globalTarget)&&((int)_globalLevel >= Trace::LEVEL_DEBUG)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PACKET_INVALID_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source);
+ if (path) {
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
+ }
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ _send(tPtr,d,_globalTarget);
+ }
+}
+
+void Trace::networkConfigRequestSent(void *const tPtr,const Network &network,const Address &controller)
+{
+ ZT_LOCAL_TRACE(tPtr,RR,"requesting configuration for network %.16llx",network.id());
+ if ((_globalTarget)&&((int)_globalLevel >= Trace::LEVEL_DEBUG)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__NETWORK_CONFIG_REQUEST_SENT_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network.id());
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_CONTROLLER_ID,controller);
+ _send(tPtr,d,_globalTarget);
+ }
+}
+
+void Trace::networkFilter(
+ void *const tPtr,
+ const Network &network,
+ const RuleResultLog &primaryRuleSetLog,
+ const RuleResultLog *const matchingCapabilityRuleSetLog,
+ const Capability *const matchingCapability,
+ const Address &ztSource,
+ const Address &ztDest,
+ const MAC &macSource,
+ const MAC &macDest,
+ const uint8_t *const frameData,
+ const unsigned int frameLen,
+ const unsigned int etherType,
+ const unsigned int vlanId,
+ const bool noTee,
+ const bool inbound,
+ const int accept)
+{
+ std::pair<Address,Trace::Level> byn;
+ { Mutex::Lock l(_byNet_m); _byNet.get(network.id(),byn); }
+
+ if ( ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_RULES)) || ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_RULES)) ) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__NETWORK_FILTER_TRACE_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network.id());
+ d.add(ZT_REMOTE_TRACE_FIELD__SOURCE_ZTADDR,ztSource);
+ d.add(ZT_REMOTE_TRACE_FIELD__DEST_ZTADDR,ztDest);
+ d.add(ZT_REMOTE_TRACE_FIELD__SOURCE_MAC,macSource.toInt());
+ d.add(ZT_REMOTE_TRACE_FIELD__DEST_MAC,macDest.toInt());
+ d.add(ZT_REMOTE_TRACE_FIELD__ETHERTYPE,(uint64_t)etherType);
+ d.add(ZT_REMOTE_TRACE_FIELD__VLAN_ID,(uint64_t)vlanId);
+ d.add(ZT_REMOTE_TRACE_FIELD__FILTER_FLAG_NOTEE,noTee ? "1" : "0");
+ d.add(ZT_REMOTE_TRACE_FIELD__FILTER_FLAG_INBOUND,inbound ? "1" : "0");
+ d.add(ZT_REMOTE_TRACE_FIELD__FILTER_RESULT,(int64_t)accept);
+ d.add(ZT_REMOTE_TRACE_FIELD__FILTER_BASE_RULE_LOG,(const char *)primaryRuleSetLog.data(),(int)primaryRuleSetLog.sizeBytes());
+ if (matchingCapabilityRuleSetLog)
+ d.add(ZT_REMOTE_TRACE_FIELD__FILTER_CAP_RULE_LOG,(const char *)matchingCapabilityRuleSetLog->data(),(int)matchingCapabilityRuleSetLog->sizeBytes());
+ if (matchingCapability)
+ d.add(ZT_REMOTE_TRACE_FIELD__FILTER_CAP_ID,(uint64_t)matchingCapability->id());
+ d.add(ZT_REMOTE_TRACE_FIELD__FRAME_LENGTH,(uint64_t)frameLen);
+ if (frameLen > 0)
+ d.add(ZT_REMOTE_TRACE_FIELD__FRAME_DATA,(const char *)frameData,(frameLen > 256) ? (int)256 : (int)frameLen);
+
+ if ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_RULES))
+ _send(tPtr,d,_globalTarget);
+ if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_RULES))
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::credentialRejected(void *const tPtr,const CertificateOfMembership &c,const char *reason)
+{
+ std::pair<Address,Trace::Level> byn;
+ if (c.networkId()) { Mutex::Lock l(_byNet_m); _byNet.get(c.networkId(),byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP,c.timestamp());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO,c.issuedTo());
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::credentialRejected(void *const tPtr,const CertificateOfOwnership &c,const char *reason)
+{
+ std::pair<Address,Trace::Level> byn;
+ if (c.networkId()) { Mutex::Lock l(_byNet_m); _byNet.get(c.networkId(),byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP,c.timestamp());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO,c.issuedTo());
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::credentialRejected(void *const tPtr,const Capability &c,const char *reason)
+{
+ std::pair<Address,Trace::Level> byn;
+ if (c.networkId()) { Mutex::Lock l(_byNet_m); _byNet.get(c.networkId(),byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP,c.timestamp());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO,c.issuedTo());
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::credentialRejected(void *const tPtr,const Tag &c,const char *reason)
+{
+ std::pair<Address,Trace::Level> byn;
+ if (c.networkId()) { Mutex::Lock l(_byNet_m); _byNet.get(c.networkId(),byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP,c.timestamp());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO,c.issuedTo());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_INFO,(uint64_t)c.value());
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::credentialRejected(void *const tPtr,const Revocation &c,const char *reason)
+{
+ std::pair<Address,Trace::Level> byn;
+ if (c.networkId()) { Mutex::Lock l(_byNet_m); _byNet.get(c.networkId(),byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_REVOCATION_TARGET,c.target());
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::updateMemoizedSettings()
+{
+ _globalTarget = RR->node->remoteTraceTarget();
+ _globalLevel = RR->node->remoteTraceLevel();
+ const std::vector< SharedPtr<Network> > nws(RR->node->allNetworks());
+ {
+ Mutex::Lock l(_byNet_m);
+ _byNet.clear();
+ for(std::vector< SharedPtr<Network> >::const_iterator n(nws.begin());n!=nws.end();++n) {
+ const Address dest((*n)->config().remoteTraceTarget);
+ if (dest) {
+ std::pair<Address,Trace::Level> &m = _byNet[(*n)->id()];
+ m.first = dest;
+ m.second = (*n)->config().remoteTraceLevel;
+ }
+ }
+ }
+}
+
+void Trace::_send(void *const tPtr,const Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> &d,const Address &dest)
+{
+ Packet outp(dest,RR->identity.address(),Packet::VERB_REMOTE_TRACE);
+ outp.appendCString(d.data());
+ outp.compress();
+ RR->sw->send(tPtr,outp,true);
+}
+
+void Trace::_spamToAllNetworks(void *const tPtr,const Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> &d,const Level level)
+{
+ Mutex::Lock l(_byNet_m);
+ Hashtable< uint64_t,std::pair< Address,Trace::Level > >::Iterator i(_byNet);
+ uint64_t *k = (uint64_t *)0;
+ std::pair<Address,Trace::Level> *v = (std::pair<Address,Trace::Level> *)0;
+ while (i.next(k,v)) {
+ if ((v)&&(v->first)&&((int)v->second >= (int)level))
+ _send(tPtr,d,v->first);
+ }
+}
+
+} // namespace ZeroTier
diff --git a/node/Trace.hpp b/node/Trace.hpp
new file mode 100644
index 00000000..2a2fca6c
--- /dev/null
+++ b/node/Trace.hpp
@@ -0,0 +1,176 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_TRACE_HPP
+#define ZT_TRACE_HPP
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "../include/ZeroTierOne.h"
+
+#include "Constants.hpp"
+#include "SharedPtr.hpp"
+#include "Packet.hpp"
+#include "Credential.hpp"
+#include "InetAddress.hpp"
+#include "Dictionary.hpp"
+#include "Mutex.hpp"
+#include "Hashtable.hpp"
+
+namespace ZeroTier {
+
+class RuntimeEnvironment;
+class Address;
+class Identity;
+class Peer;
+class Path;
+class Network;
+class NetworkConfig;
+class MAC;
+class CertificateOfMembership;
+class CertificateOfOwnership;
+class Revocation;
+class Tag;
+class Capability;
+
+/**
+ * Remote tracing and trace logging handler
+ */
+class Trace
+{
+public:
+ /**
+ * Trace verbosity level
+ */
+ enum Level
+ {
+ LEVEL_NORMAL = 0,
+ LEVEL_VERBOSE = 10,
+ LEVEL_RULES = 15,
+ LEVEL_DEBUG = 20,
+ LEVEL_INSANE = 30
+ };
+
+ /**
+ * Filter rule evaluation result log
+ *
+ * Each rule in a rule set gets a four-bit log entry. A log entry
+ * of zero means not evaluated. Otherwise each four-bit log entry
+ * contains two two-bit values of 01 for 'false' and 10 for 'true'.
+ * As with four-bit rules an 00 value here means this was not
+ * evaluated or was not relevant.
+ */
+ class RuleResultLog
+ {
+ public:
+ RuleResultLog() {}
+
+ inline void log(const unsigned int rn,const uint8_t thisRuleMatches,const uint8_t thisSetMatches)
+ {
+ _l[rn >> 1] |= ( ((thisRuleMatches + 1) << 2) | (thisSetMatches + 1) ) << ((rn & 1) << 2);
+ }
+ inline void logSkipped(const unsigned int rn,const uint8_t thisSetMatches)
+ {
+ _l[rn >> 1] |= (thisSetMatches + 1) << ((rn & 1) << 2);
+ }
+
+ inline void clear()
+ {
+ memset(_l,0,sizeof(_l));
+ }
+
+ inline const uint8_t *data() const { return _l; }
+ inline unsigned int sizeBytes() const { return (ZT_MAX_NETWORK_RULES / 2); }
+
+ private:
+ uint8_t _l[ZT_MAX_NETWORK_RULES / 2];
+ };
+
+ Trace(const RuntimeEnvironment *renv) :
+ RR(renv),
+ _byNet(8)
+ {
+ }
+
+ void resettingPathsInScope(void *const tPtr,const Address &reporter,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,const InetAddress::IpScope scope);
+
+ void peerConfirmingUnknownPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &path,const uint64_t packetId,const Packet::Verb verb);
+ void peerLearnedNewPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &newPath,const uint64_t packetId);
+ void peerRedirected(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &newPath);
+
+ void incomingPacketMessageAuthenticationFailure(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const unsigned int hops,const char *reason);
+ void incomingPacketInvalid(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const unsigned int hops,const Packet::Verb verb,const char *reason);
+ void incomingPacketDroppedHELLO(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const char *reason);
+
+ void outgoingNetworkFrameDropped(void *const tPtr,const SharedPtr<Network> &network,const MAC &sourceMac,const MAC &destMac,const unsigned int etherType,const unsigned int vlanId,const unsigned int frameLen,const char *reason);
+ void incomingNetworkAccessDenied(void *const tPtr,const SharedPtr<Network> &network,const SharedPtr<Path> &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,bool credentialsRequested);
+ void incomingNetworkFrameDropped(void *const tPtr,const SharedPtr<Network> &network,const SharedPtr<Path> &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,const MAC &sourceMac,const MAC &destMac,const char *reason);
+
+ void networkConfigRequestSent(void *const tPtr,const Network &network,const Address &controller);
+ void networkFilter(
+ void *const tPtr,
+ const Network &network,
+ const RuleResultLog &primaryRuleSetLog,
+ const RuleResultLog *const matchingCapabilityRuleSetLog,
+ const Capability *const matchingCapability,
+ const Address &ztSource,
+ const Address &ztDest,
+ const MAC &macSource,
+ const MAC &macDest,
+ const uint8_t *const frameData,
+ const unsigned int frameLen,
+ const unsigned int etherType,
+ const unsigned int vlanId,
+ const bool noTee,
+ const bool inbound,
+ const int accept);
+
+ void credentialRejected(void *const tPtr,const CertificateOfMembership &c,const char *reason);
+ void credentialRejected(void *const tPtr,const CertificateOfOwnership &c,const char *reason);
+ void credentialRejected(void *const tPtr,const Capability &c,const char *reason);
+ void credentialRejected(void *const tPtr,const Tag &c,const char *reason);
+ void credentialRejected(void *const tPtr,const Revocation &c,const char *reason);
+
+ void updateMemoizedSettings();
+
+private:
+ const RuntimeEnvironment *const RR;
+
+ void _send(void *const tPtr,const Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> &d,const Address &dest);
+ void _spamToAllNetworks(void *const tPtr,const Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> &d,const Level level);
+
+ Address _globalTarget;
+ Trace::Level _globalLevel;
+ Hashtable< uint64_t,std::pair< Address,Trace::Level > > _byNet;
+ Mutex _byNet_m;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/Utils.cpp b/node/Utils.cpp
index 9ce1bf05..a69a575e 100644
--- a/node/Utils.cpp
+++ b/node/Utils.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -56,80 +64,25 @@ static void _Utils_doBurn(volatile uint8_t *ptr,unsigned int len)
static void (*volatile _Utils_doBurn_ptr)(volatile uint8_t *,unsigned int) = _Utils_doBurn;
void Utils::burn(void *ptr,unsigned int len) { (_Utils_doBurn_ptr)((volatile uint8_t *)ptr,len); }
-std::string Utils::hex(const void *data,unsigned int len)
+static unsigned long _Utils_itoa(unsigned long n,char *s)
{
- std::string r;
- r.reserve(len * 2);
- for(unsigned int i=0;i<len;++i) {
- r.push_back(HEXCHARS[(((const unsigned char *)data)[i] & 0xf0) >> 4]);
- r.push_back(HEXCHARS[((const unsigned char *)data)[i] & 0x0f]);
- }
- return r;
+ if (n == 0)
+ return 0;
+ unsigned long pos = _Utils_itoa(n / 10,s);
+ if (pos >= 22) // sanity check, should be impossible
+ pos = 22;
+ s[pos] = '0' + (char)(n % 10);
+ return pos + 1;
}
-
-std::string Utils::unhex(const char *hex,unsigned int maxlen)
+char *Utils::decimal(unsigned long n,char s[24])
{
- int n = 1;
- unsigned char c,b = 0;
- const char *eof = hex + maxlen;
- std::string r;
-
- if (!maxlen)
- return r;
-
- while ((c = (unsigned char)*(hex++))) {
- if ((c >= 48)&&(c <= 57)) { // 0..9
- if ((n ^= 1))
- r.push_back((char)(b | (c - 48)));
- else b = (c - 48) << 4;
- } else if ((c >= 65)&&(c <= 70)) { // A..F
- if ((n ^= 1))
- r.push_back((char)(b | (c - (65 - 10))));
- else b = (c - (65 - 10)) << 4;
- } else if ((c >= 97)&&(c <= 102)) { // a..f
- if ((n ^= 1))
- r.push_back((char)(b | (c - (97 - 10))));
- else b = (c - (97 - 10)) << 4;
- }
- if (hex == eof)
- break;
+ if (n == 0) {
+ s[0] = '0';
+ s[1] = (char)0;
+ return s;
}
-
- return r;
-}
-
-unsigned int Utils::unhex(const char *hex,unsigned int maxlen,void *buf,unsigned int len)
-{
- int n = 1;
- unsigned char c,b = 0;
- unsigned int l = 0;
- const char *eof = hex + maxlen;
-
- if (!maxlen)
- return 0;
-
- while ((c = (unsigned char)*(hex++))) {
- if ((c >= 48)&&(c <= 57)) { // 0..9
- if ((n ^= 1)) {
- if (l >= len) break;
- ((unsigned char *)buf)[l++] = (b | (c - 48));
- } else b = (c - 48) << 4;
- } else if ((c >= 65)&&(c <= 70)) { // A..F
- if ((n ^= 1)) {
- if (l >= len) break;
- ((unsigned char *)buf)[l++] = (b | (c - (65 - 10)));
- } else b = (c - (65 - 10)) << 4;
- } else if ((c >= 97)&&(c <= 102)) { // a..f
- if ((n ^= 1)) {
- if (l >= len) break;
- ((unsigned char *)buf)[l++] = (b | (c - (97 - 10)));
- } else b = (c - (97 - 10)) << 4;
- }
- if (hex == eof)
- break;
- }
-
- return l;
+ s[_Utils_itoa(n,s)] = (char)0;
+ return s;
}
void Utils::getSecureRandom(void *buf,unsigned int bytes)
@@ -218,40 +171,4 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
#endif // __WINDOWS__ or not
}
-bool Utils::scopy(char *dest,unsigned int len,const char *src)
-{
- if (!len)
- return false; // sanity check
- if (!src) {
- *dest = (char)0;
- return true;
- }
- char *end = dest + len;
- while ((*dest++ = *src++)) {
- if (dest == end) {
- *(--dest) = (char)0;
- return false;
- }
- }
- return true;
-}
-
-unsigned int Utils::snprintf(char *buf,unsigned int len,const char *fmt,...)
- throw(std::length_error)
-{
- va_list ap;
-
- va_start(ap,fmt);
- int n = (int)vsnprintf(buf,len,fmt,ap);
- va_end(ap);
-
- if ((n >= (int)len)||(n < 0)) {
- if (len)
- buf[len - 1] = (char)0;
- throw std::length_error("buf[] overflow in Utils::snprintf");
- }
-
- return (unsigned int)n;
-}
-
} // namespace ZeroTier
diff --git a/node/Utils.hpp b/node/Utils.hpp
index ceb29d7e..a24f2c9a 100644
--- a/node/Utils.hpp
+++ b/node/Utils.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_UTILS_HPP
@@ -32,6 +40,44 @@
#include "Constants.hpp"
+#ifdef __LINUX__
+//#if (defined(_MSC_VER) || defined(__GNUC__)) && (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64))
+#if 0
+#include <emmintrin.h>
+static inline void ZT_FAST_MEMCPY(void *a,const void *b,unsigned long k)
+{
+ char *aa = reinterpret_cast<char *>(a);
+ const char *bb = reinterpret_cast<const char *>(b);
+ while (k >= 64) {
+ __m128 t1 = _mm_loadu_ps(reinterpret_cast<const float *>(bb));
+ __m128 t2 = _mm_loadu_ps(reinterpret_cast<const float *>(bb + 16));
+ __m128 t3 = _mm_loadu_ps(reinterpret_cast<const float *>(bb + 32));
+ __m128 t4 = _mm_loadu_ps(reinterpret_cast<const float *>(bb + 48));
+ _mm_storeu_ps(reinterpret_cast<float *>(aa),t1);
+ _mm_storeu_ps(reinterpret_cast<float *>(aa + 16),t2);
+ _mm_storeu_ps(reinterpret_cast<float *>(aa + 32),t3);
+ _mm_storeu_ps(reinterpret_cast<float *>(aa + 48),t4);
+ bb += 64;
+ aa += 64;
+ k -= 64;
+ }
+ while (k >= 16) {
+ __m128 t1 = _mm_loadu_ps(reinterpret_cast<const float *>(bb));
+ _mm_storeu_ps(reinterpret_cast<float *>(aa),t1);
+ bb += 16;
+ aa += 16;
+ k -= 16;
+ }
+ for(unsigned long i=0;i<k;++i)
+ aa[i] = bb[i];
+}
+#else
+#define ZT_FAST_MEMCPY(a,b,c) memcpy(a,b,c)
+#endif
+#else
+#define ZT_FAST_MEMCPY(a,b,c) memcpy(a,b,c)
+#endif
+
namespace ZeroTier {
/**
@@ -62,42 +108,158 @@ public:
static void burn(void *ptr,unsigned int len);
/**
- * Convert binary data to hexadecimal
- *
- * @param data Data to convert to hex
- * @param len Length of data
- * @return Hexadecimal string
+ * @param n Number to convert
+ * @param s Buffer, at least 24 bytes in size
+ * @return String containing 'n' in base 10 form
*/
- static std::string hex(const void *data,unsigned int len);
- static inline std::string hex(const std::string &data) { return hex(data.data(),(unsigned int)data.length()); }
+ static char *decimal(unsigned long n,char s[24]);
- /**
- * Convert hexadecimal to binary data
- *
- * This ignores all non-hex characters, just stepping over them and
- * continuing. Upper and lower case are supported for letters a-f.
- *
- * @param hex Hexadecimal ASCII code (non-hex chars are ignored, stops at zero or maxlen)
- * @param maxlen Maximum length of hex string buffer
- * @return Binary data
- */
- static std::string unhex(const char *hex,unsigned int maxlen);
- static inline std::string unhex(const std::string &hex) { return unhex(hex.c_str(),(unsigned int)hex.length()); }
+ static inline char *hex(uint64_t i,char s[17])
+ {
+ s[0] = HEXCHARS[(i >> 60) & 0xf];
+ s[1] = HEXCHARS[(i >> 56) & 0xf];
+ s[2] = HEXCHARS[(i >> 52) & 0xf];
+ s[3] = HEXCHARS[(i >> 48) & 0xf];
+ s[4] = HEXCHARS[(i >> 44) & 0xf];
+ s[5] = HEXCHARS[(i >> 40) & 0xf];
+ s[6] = HEXCHARS[(i >> 36) & 0xf];
+ s[7] = HEXCHARS[(i >> 32) & 0xf];
+ s[8] = HEXCHARS[(i >> 28) & 0xf];
+ s[9] = HEXCHARS[(i >> 24) & 0xf];
+ s[10] = HEXCHARS[(i >> 20) & 0xf];
+ s[11] = HEXCHARS[(i >> 16) & 0xf];
+ s[12] = HEXCHARS[(i >> 12) & 0xf];
+ s[13] = HEXCHARS[(i >> 8) & 0xf];
+ s[14] = HEXCHARS[(i >> 4) & 0xf];
+ s[15] = HEXCHARS[i & 0xf];
+ s[16] = (char)0;
+ return s;
+ }
- /**
- * Convert hexadecimal to binary data
- *
- * This ignores all non-hex characters, just stepping over them and
- * continuing. Upper and lower case are supported for letters a-f.
- *
- * @param hex Hexadecimal ASCII
- * @param maxlen Maximum length of hex string buffer
- * @param buf Buffer to fill
- * @param len Length of buffer
- * @return Number of characters actually written
- */
- static unsigned int unhex(const char *hex,unsigned int maxlen,void *buf,unsigned int len);
- static inline unsigned int unhex(const std::string &hex,void *buf,unsigned int len) { return unhex(hex.c_str(),(unsigned int)hex.length(),buf,len); }
+ static inline char *hex10(uint64_t i,char s[11])
+ {
+ s[0] = HEXCHARS[(i >> 36) & 0xf];
+ s[1] = HEXCHARS[(i >> 32) & 0xf];
+ s[2] = HEXCHARS[(i >> 28) & 0xf];
+ s[3] = HEXCHARS[(i >> 24) & 0xf];
+ s[4] = HEXCHARS[(i >> 20) & 0xf];
+ s[5] = HEXCHARS[(i >> 16) & 0xf];
+ s[6] = HEXCHARS[(i >> 12) & 0xf];
+ s[7] = HEXCHARS[(i >> 8) & 0xf];
+ s[8] = HEXCHARS[(i >> 4) & 0xf];
+ s[9] = HEXCHARS[i & 0xf];
+ s[10] = (char)0;
+ return s;
+ }
+
+ static inline char *hex(uint32_t i,char s[9])
+ {
+ s[0] = HEXCHARS[(i >> 28) & 0xf];
+ s[1] = HEXCHARS[(i >> 24) & 0xf];
+ s[2] = HEXCHARS[(i >> 20) & 0xf];
+ s[3] = HEXCHARS[(i >> 16) & 0xf];
+ s[4] = HEXCHARS[(i >> 12) & 0xf];
+ s[5] = HEXCHARS[(i >> 8) & 0xf];
+ s[6] = HEXCHARS[(i >> 4) & 0xf];
+ s[7] = HEXCHARS[i & 0xf];
+ s[8] = (char)0;
+ return s;
+ }
+
+ static inline char *hex(uint16_t i,char s[5])
+ {
+ s[0] = HEXCHARS[(i >> 12) & 0xf];
+ s[1] = HEXCHARS[(i >> 8) & 0xf];
+ s[2] = HEXCHARS[(i >> 4) & 0xf];
+ s[3] = HEXCHARS[i & 0xf];
+ s[4] = (char)0;
+ return s;
+ }
+
+ static inline char *hex(uint8_t i,char s[3])
+ {
+ s[0] = HEXCHARS[(i >> 4) & 0xf];
+ s[1] = HEXCHARS[i & 0xf];
+ s[2] = (char)0;
+ return s;
+ }
+
+ static inline char *hex(const void *d,unsigned int l,char *s)
+ {
+ char *const save = s;
+ for(unsigned int i=0;i<l;++i) {
+ const unsigned int b = reinterpret_cast<const uint8_t *>(d)[i];
+ *(s++) = HEXCHARS[b >> 4];
+ *(s++) = HEXCHARS[b & 0xf];
+ }
+ *s = (char)0;
+ return save;
+ }
+
+ static inline unsigned int unhex(const char *h,void *buf,unsigned int buflen)
+ {
+ unsigned int l = 0;
+ while (l < buflen) {
+ uint8_t hc = *(reinterpret_cast<const uint8_t *>(h++));
+ if (!hc) break;
+
+ uint8_t c = 0;
+ if ((hc >= 48)&&(hc <= 57)) // 0..9
+ c = hc - 48;
+ else if ((hc >= 97)&&(hc <= 102)) // a..f
+ c = hc - 87;
+ else if ((hc >= 65)&&(hc <= 70)) // A..F
+ c = hc - 55;
+
+ hc = *(reinterpret_cast<const uint8_t *>(h++));
+ if (!hc) break;
+
+ c <<= 4;
+ if ((hc >= 48)&&(hc <= 57))
+ c |= hc - 48;
+ else if ((hc >= 97)&&(hc <= 102))
+ c |= hc - 87;
+ else if ((hc >= 65)&&(hc <= 70))
+ c |= hc - 55;
+
+ reinterpret_cast<uint8_t *>(buf)[l++] = c;
+ }
+ return l;
+ }
+
+ static inline unsigned int unhex(const char *h,unsigned int hlen,void *buf,unsigned int buflen)
+ {
+ unsigned int l = 0;
+ const char *hend = h + hlen;
+ while (l < buflen) {
+ if (h == hend) break;
+ uint8_t hc = *(reinterpret_cast<const uint8_t *>(h++));
+ if (!hc) break;
+
+ uint8_t c = 0;
+ if ((hc >= 48)&&(hc <= 57))
+ c = hc - 48;
+ else if ((hc >= 97)&&(hc <= 102))
+ c = hc - 87;
+ else if ((hc >= 65)&&(hc <= 70))
+ c = hc - 55;
+
+ if (h == hend) break;
+ hc = *(reinterpret_cast<const uint8_t *>(h++));
+ if (!hc) break;
+
+ c <<= 4;
+ if ((hc >= 48)&&(hc <= 57))
+ c |= hc - 48;
+ else if ((hc >= 97)&&(hc <= 102))
+ c |= hc - 87;
+ else if ((hc >= 65)&&(hc <= 70))
+ c |= hc - 55;
+
+ reinterpret_cast<uint8_t *>(buf)[l++] = c;
+ }
+ return l;
+ }
/**
* Generate secure random bytes
@@ -118,7 +280,6 @@ public:
* @param saveptr Pointer to a char * for temporary reentrant storage
*/
static inline char *stok(char *str,const char *delim,char **saveptr)
- throw()
{
#ifdef __WINDOWS__
return strtok_s(str,delim,saveptr);
@@ -127,30 +288,11 @@ public:
#endif
}
- // String to number converters -- defined here to permit portability
- // ifdefs for platforms that lack some of the strtoXX functions.
- static inline unsigned int strToUInt(const char *s)
- throw()
- {
- return (unsigned int)strtoul(s,(char **)0,10);
- }
- static inline int strToInt(const char *s)
- throw()
- {
- return (int)strtol(s,(char **)0,10);
- }
- static inline unsigned long strToULong(const char *s)
- throw()
- {
- return strtoul(s,(char **)0,10);
- }
- static inline long strToLong(const char *s)
- throw()
- {
- return strtol(s,(char **)0,10);
- }
+ static inline unsigned int strToUInt(const char *s) { return (unsigned int)strtoul(s,(char **)0,10); }
+ static inline int strToInt(const char *s) { return (int)strtol(s,(char **)0,10); }
+ static inline unsigned long strToULong(const char *s) { return strtoul(s,(char **)0,10); }
+ static inline long strToLong(const char *s) { return strtol(s,(char **)0,10); }
static inline unsigned long long strToU64(const char *s)
- throw()
{
#ifdef __WINDOWS__
return (unsigned long long)_strtoui64(s,(char **)0,10);
@@ -159,7 +301,6 @@ public:
#endif
}
static inline long long strTo64(const char *s)
- throw()
{
#ifdef __WINDOWS__
return (long long)_strtoi64(s,(char **)0,10);
@@ -167,28 +308,11 @@ public:
return strtoll(s,(char **)0,10);
#endif
}
- static inline unsigned int hexStrToUInt(const char *s)
- throw()
- {
- return (unsigned int)strtoul(s,(char **)0,16);
- }
- static inline int hexStrToInt(const char *s)
- throw()
- {
- return (int)strtol(s,(char **)0,16);
- }
- static inline unsigned long hexStrToULong(const char *s)
- throw()
- {
- return strtoul(s,(char **)0,16);
- }
- static inline long hexStrToLong(const char *s)
- throw()
- {
- return strtol(s,(char **)0,16);
- }
+ static inline unsigned int hexStrToUInt(const char *s) { return (unsigned int)strtoul(s,(char **)0,16); }
+ static inline int hexStrToInt(const char *s) { return (int)strtol(s,(char **)0,16); }
+ static inline unsigned long hexStrToULong(const char *s) { return strtoul(s,(char **)0,16); }
+ static inline long hexStrToLong(const char *s) { return strtol(s,(char **)0,16); }
static inline unsigned long long hexStrToU64(const char *s)
- throw()
{
#ifdef __WINDOWS__
return (unsigned long long)_strtoui64(s,(char **)0,16);
@@ -197,7 +321,6 @@ public:
#endif
}
static inline long long hexStrTo64(const char *s)
- throw()
{
#ifdef __WINDOWS__
return (long long)_strtoi64(s,(char **)0,16);
@@ -205,11 +328,6 @@ public:
return strtoll(s,(char **)0,16);
#endif
}
- static inline double strToDouble(const char *s)
- throw()
- {
- return strtod(s,(char **)0);
- }
/**
* Perform a safe C string copy, ALWAYS null-terminating the result
@@ -222,22 +340,23 @@ public:
* @param src Source string (if NULL, dest will receive a zero-length string and true is returned)
* @return True on success, false on overflow (buffer will still be 0-terminated)
*/
- static bool scopy(char *dest,unsigned int len,const char *src);
-
- /**
- * Variant of snprintf that is portable and throws an exception
- *
- * This just wraps the local implementation whatever it's called, while
- * performing a few other checks and adding exceptions for overflow.
- *
- * @param buf Buffer to write to
- * @param len Length of buffer in bytes
- * @param fmt Format string
- * @param ... Format arguments
- * @throws std::length_error buf[] too short (buf[] will still be left null-terminated)
- */
- static unsigned int snprintf(char *buf,unsigned int len,const char *fmt,...)
- throw(std::length_error);
+ static inline bool scopy(char *dest,unsigned int len,const char *src)
+ {
+ if (!len)
+ return false; // sanity check
+ if (!src) {
+ *dest = (char)0;
+ return true;
+ }
+ char *end = dest + len;
+ while ((*dest++ = *src++)) {
+ if (dest == end) {
+ *(--dest) = (char)0;
+ return false;
+ }
+ }
+ return true;
+ }
/**
* Count the number of bits set in an integer
@@ -283,14 +402,13 @@ public:
}
// Byte swappers for big/little endian conversion
- static inline uint8_t hton(uint8_t n) throw() { return n; }
- static inline int8_t hton(int8_t n) throw() { return n; }
- static inline uint16_t hton(uint16_t n) throw() { return htons(n); }
- static inline int16_t hton(int16_t n) throw() { return (int16_t)htons((uint16_t)n); }
- static inline uint32_t hton(uint32_t n) throw() { return htonl(n); }
- static inline int32_t hton(int32_t n) throw() { return (int32_t)htonl((uint32_t)n); }
+ static inline uint8_t hton(uint8_t n) { return n; }
+ static inline int8_t hton(int8_t n) { return n; }
+ static inline uint16_t hton(uint16_t n) { return htons(n); }
+ static inline int16_t hton(int16_t n) { return (int16_t)htons((uint16_t)n); }
+ static inline uint32_t hton(uint32_t n) { return htonl(n); }
+ static inline int32_t hton(int32_t n) { return (int32_t)htonl((uint32_t)n); }
static inline uint64_t hton(uint64_t n)
- throw()
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
#if defined(__GNUC__) && (!defined(__OpenBSD__))
@@ -311,16 +429,15 @@ public:
return n;
#endif
}
- static inline int64_t hton(int64_t n) throw() { return (int64_t)hton((uint64_t)n); }
-
- static inline uint8_t ntoh(uint8_t n) throw() { return n; }
- static inline int8_t ntoh(int8_t n) throw() { return n; }
- static inline uint16_t ntoh(uint16_t n) throw() { return ntohs(n); }
- static inline int16_t ntoh(int16_t n) throw() { return (int16_t)ntohs((uint16_t)n); }
- static inline uint32_t ntoh(uint32_t n) throw() { return ntohl(n); }
- static inline int32_t ntoh(int32_t n) throw() { return (int32_t)ntohl((uint32_t)n); }
+ static inline int64_t hton(int64_t n) { return (int64_t)hton((uint64_t)n); }
+
+ static inline uint8_t ntoh(uint8_t n) { return n; }
+ static inline int8_t ntoh(int8_t n) { return n; }
+ static inline uint16_t ntoh(uint16_t n) { return ntohs(n); }
+ static inline int16_t ntoh(int16_t n) { return (int16_t)ntohs((uint16_t)n); }
+ static inline uint32_t ntoh(uint32_t n) { return ntohl(n); }
+ static inline int32_t ntoh(int32_t n) { return (int32_t)ntohl((uint32_t)n); }
static inline uint64_t ntoh(uint64_t n)
- throw()
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
#if defined(__GNUC__) && !defined(__OpenBSD__)
@@ -341,39 +458,7 @@ public:
return n;
#endif
}
- static inline int64_t ntoh(int64_t n) throw() { return (int64_t)ntoh((uint64_t)n); }
-
- /**
- * Compare Peer version tuples
- *
- * @return -1, 0, or 1 based on whether first tuple is less than, equal to, or greater than second
- */
- static inline int compareVersion(unsigned int maj1,unsigned int min1,unsigned int rev1,unsigned int b1,unsigned int maj2,unsigned int min2,unsigned int rev2,unsigned int b2)
- {
- if (maj1 > maj2)
- return 1;
- else if (maj1 < maj2)
- return -1;
- else {
- if (min1 > min2)
- return 1;
- else if (min1 < min2)
- return -1;
- else {
- if (rev1 > rev2)
- return 1;
- else if (rev1 < rev2)
- return -1;
- else {
- if (b1 > b2)
- return 1;
- else if (b1 < b2)
- return -1;
- else return 0;
- }
- }
- }
- }
+ static inline int64_t ntoh(int64_t n) { return (int64_t)ntoh((uint64_t)n); }
/**
* Hexadecimal characters 0-f
diff --git a/node/World.hpp b/node/World.hpp
index 6e835bec..9edf2a6a 100644
--- a/node/World.hpp
+++ b/node/World.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_WORLD_HPP
@@ -102,9 +110,9 @@ public:
Identity identity;
std::vector<InetAddress> stableEndpoints;
- inline bool operator==(const Root &r) const throw() { return ((identity == r.identity)&&(stableEndpoints == r.stableEndpoints)); }
- inline bool operator!=(const Root &r) const throw() { return (!(*this == r)); }
- inline bool operator<(const Root &r) const throw() { return (identity < r.identity); } // for sorting
+ inline bool operator==(const Root &r) const { return ((identity == r.identity)&&(stableEndpoints == r.stableEndpoints)); }
+ inline bool operator!=(const Root &r) const { return (!(*this == r)); }
+ inline bool operator<(const Root &r) const { return (identity < r.identity); } // for sorting
};
/**
@@ -204,23 +212,23 @@ public:
case TYPE_PLANET: _type = TYPE_PLANET; break;
case TYPE_MOON: _type = TYPE_MOON; break;
default:
- throw std::invalid_argument("invalid world type");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
}
_id = b.template at<uint64_t>(p); p += 8;
_ts = b.template at<uint64_t>(p); p += 8;
- memcpy(_updatesMustBeSignedBy.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN); p += ZT_C25519_PUBLIC_KEY_LEN;
- memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
+ ZT_FAST_MEMCPY(_updatesMustBeSignedBy.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN); p += ZT_C25519_PUBLIC_KEY_LEN;
+ ZT_FAST_MEMCPY(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
const unsigned int numRoots = (unsigned int)b[p++];
if (numRoots > ZT_WORLD_MAX_ROOTS)
- throw std::invalid_argument("too many roots in World");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
for(unsigned int k=0;k<numRoots;++k) {
_roots.push_back(Root());
Root &r = _roots.back();
p += r.identity.deserialize(b,p);
unsigned int numStableEndpoints = b[p++];
if (numStableEndpoints > ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT)
- throw std::invalid_argument("too many stable endpoints in World/Root");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
for(unsigned int kk=0;kk<numStableEndpoints;++kk) {
r.stableEndpoints.push_back(InetAddress());
p += r.stableEndpoints.back().deserialize(b,p);
@@ -232,11 +240,9 @@ public:
return (p - startAt);
}
- inline bool operator==(const World &w) const { return ((_id == w._id)&&(_ts == w._ts)&&(_updatesMustBeSignedBy == w._updatesMustBeSignedBy)&&(_signature == w._signature)&&(_roots == w._roots)&&(_type == w._type)); }
+ inline bool operator==(const World &w) const { return ((_id == w._id)&&(_ts == w._ts)&&(memcmp(_updatesMustBeSignedBy.data,w._updatesMustBeSignedBy.data,ZT_C25519_PUBLIC_KEY_LEN) == 0)&&(memcmp(_signature.data,w._signature.data,ZT_C25519_SIGNATURE_LEN) == 0)&&(_roots == w._roots)&&(_type == w._type)); }
inline bool operator!=(const World &w) const { return (!(*this == w)); }
- inline bool operator<(const World &w) const { return (((int)_type < (int)w._type) ? true : ((_type == w._type) ? (_id < w._id) : false)); }
-
/**
* Create a World object signed with a key pair
*
diff --git a/objects.mk b/objects.mk
index 74efc337..d4a3c16c 100644
--- a/objects.mk
+++ b/objects.mk
@@ -1,11 +1,8 @@
-OBJS=\
- controller/EmbeddedNetworkController.o \
- controller/JSONDB.o \
+CORE_OBJS=\
node/C25519.o \
node/Capability.o \
node/CertificateOfMembership.o \
node/CertificateOfOwnership.o \
- node/Cluster.o \
node/Identity.o \
node/IncomingPacket.o \
node/InetAddress.o \
@@ -26,9 +23,17 @@ OBJS=\
node/Switch.o \
node/Tag.o \
node/Topology.o \
- node/Utils.o \
+ node/Trace.o \
+ node/Utils.o
+
+ONE_OBJS=\
+ controller/EmbeddedNetworkController.o \
+ controller/DB.o \
+ controller/FileDB.o \
+ controller/RethinkDB.o \
osdep/ManagedRoute.o \
osdep/Http.o \
osdep/OSUtils.o \
- service/ClusterGeoIpService.o \
- service/SoftwareUpdater.o
+ service/SoftwareUpdater.o \
+ service/OneService.o
+
diff --git a/one.cpp b/one.cpp
index 495c3147..06944494 100644
--- a/one.cpp
+++ b/one.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -85,7 +93,7 @@ using namespace ZeroTier;
static OneService *volatile zt1Service = (OneService *)0;
#define PROGRAM_NAME "ZeroTier One"
-#define COPYRIGHT_NOTICE "Copyright (c) 2011-2017 ZeroTier, Inc."
+#define COPYRIGHT_NOTICE "Copyright (c) 2011-2018 ZeroTier, Inc."
#define LICENSE_GRANT \
"This is free software: you may copy, modify, and/or distribute this" ZT_EOL_S \
"work under the terms of the GNU General Public License, version 3 or" ZT_EOL_S \
@@ -123,6 +131,7 @@ static void cliPrintHelp(const char *pn,FILE *out)
fprintf(out," join <network> - Join a network" ZT_EOL_S);
fprintf(out," leave <network> - Leave a network" ZT_EOL_S);
fprintf(out," set <network> <setting> - Set a network setting" ZT_EOL_S);
+ fprintf(out," get <network> <setting> - Get a network setting" ZT_EOL_S);
fprintf(out," listmoons - List moons (federated root sets)" ZT_EOL_S);
fprintf(out," orbit <world ID> <seed> - Join a moon via any member root" ZT_EOL_S);
fprintf(out," deorbit <world ID> - Leave a moon" ZT_EOL_S);
@@ -252,9 +261,9 @@ static int cli(int argc,char **argv)
if (hd) {
char p[4096];
#ifdef __APPLE__
- Utils::snprintf(p,sizeof(p),"%s/Library/Application Support/ZeroTier/One/authtoken.secret",hd);
+ OSUtils::ztsnprintf(p,sizeof(p),"%s/Library/Application Support/ZeroTier/One/authtoken.secret",hd);
#else
- Utils::snprintf(p,sizeof(p),"%s/.zeroTierOneAuthToken",hd);
+ OSUtils::ztsnprintf(p,sizeof(p),"%s/.zeroTierOneAuthToken",hd);
#endif
OSUtils::readFile(p,authToken);
}
@@ -270,7 +279,7 @@ static int cli(int argc,char **argv)
InetAddress addr;
{
char addrtmp[256];
- Utils::snprintf(addrtmp,sizeof(addrtmp),"%s/%u",ip.c_str(),port);
+ OSUtils::ztsnprintf(addrtmp,sizeof(addrtmp),"%s/%u",ip.c_str(),port);
addr = InetAddress(addrtmp);
}
@@ -290,7 +299,7 @@ static int cli(int argc,char **argv)
responseHeaders,
responseBody);
if (scode == 200) {
- printf("%s",cliFixJsonCRs(responseBody).c_str());
+ printf("%s", cliFixJsonCRs(responseBody).c_str());
return 0;
} else {
printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
@@ -299,6 +308,11 @@ static int cli(int argc,char **argv)
} else if ((command == "info")||(command == "status")) {
const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/status",requestHeaders,responseHeaders,responseBody);
+ if (scode == 0) {
+ printf("Error connecting to the ZeroTier service: %s\n\nPlease check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S, responseBody.c_str());
+ return 1;
+ }
+
nlohmann::json j;
try {
j = OSUtils::jsonParse(responseBody);
@@ -329,6 +343,11 @@ static int cli(int argc,char **argv)
} else if (command == "listpeers") {
const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/peer",requestHeaders,responseHeaders,responseBody);
+ if (scode == 0) {
+ printf("Error connecting to the ZeroTier service: %s\n\nPlease check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S, responseBody.c_str());
+ return 1;
+ }
+
nlohmann::json j;
try {
j = OSUtils::jsonParse(responseBody);
@@ -356,9 +375,8 @@ static int cli(int argc,char **argv)
if (path["preferred"]) {
char tmp[256];
std::string addr = path["address"];
- const uint64_t now = OSUtils::now();
- const double lq = (path.count("linkQuality")) ? (double)path["linkQuality"] : -1.0;
- Utils::snprintf(tmp,sizeof(tmp),"%s;%llu;%llu;%1.2f",addr.c_str(),now - (uint64_t)path["lastSend"],now - (uint64_t)path["lastReceive"],lq);
+ const int64_t now = OSUtils::now();
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%s;%lld;%lld",addr.c_str(),now - (int64_t)path["lastSend"],now - (int64_t)path["lastReceive"]);
bestPath = tmp;
break;
}
@@ -370,7 +388,7 @@ static int cli(int argc,char **argv)
int64_t vmin = p["versionMinor"];
int64_t vrev = p["versionRev"];
if (vmaj >= 0) {
- Utils::snprintf(ver,sizeof(ver),"%lld.%lld.%lld",vmaj,vmin,vrev);
+ OSUtils::ztsnprintf(ver,sizeof(ver),"%lld.%lld.%lld",vmaj,vmin,vrev);
} else {
ver[0] = '-';
ver[1] = (char)0;
@@ -392,6 +410,11 @@ static int cli(int argc,char **argv)
} else if (command == "listnetworks") {
const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/network",requestHeaders,responseHeaders,responseBody);
+ if (scode == 0) {
+ printf("Error connecting to the ZeroTier service: %s\n\nPlease check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S, responseBody.c_str());
+ return 1;
+ }
+
nlohmann::json j;
try {
j = OSUtils::jsonParse(responseBody);
@@ -496,6 +519,11 @@ static int cli(int argc,char **argv)
} else if (command == "listmoons") {
const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/moon",requestHeaders,responseHeaders,responseBody);
+ if (scode == 0) {
+ printf("Error connecting to the ZeroTier service: %s\n\nPlease check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S, responseBody.c_str());
+ return 1;
+ }
+
nlohmann::json j;
try {
j = OSUtils::jsonParse(responseBody);
@@ -519,9 +547,9 @@ static int cli(int argc,char **argv)
const uint64_t seed = Utils::hexStrToU64(arg2.c_str());
if ((worldId)&&(seed)) {
char jsons[1024];
- Utils::snprintf(jsons,sizeof(jsons),"{\"seed\":\"%s\"}",arg2.c_str());
+ OSUtils::ztsnprintf(jsons,sizeof(jsons),"{\"seed\":\"%s\"}",arg2.c_str());
char cl[128];
- Utils::snprintf(cl,sizeof(cl),"%u",(unsigned int)strlen(jsons));
+ OSUtils::ztsnprintf(cl,sizeof(cl),"%u",(unsigned int)strlen(jsons));
requestHeaders["Content-Type"] = "application/json";
requestHeaders["Content-Length"] = cl;
unsigned int scode = Http::POST(
@@ -571,11 +599,11 @@ static int cli(int argc,char **argv)
if (eqidx != std::string::npos) {
if ((arg2.substr(0,eqidx) == "allowManaged")||(arg2.substr(0,eqidx) == "allowGlobal")||(arg2.substr(0,eqidx) == "allowDefault")) {
char jsons[1024];
- Utils::snprintf(jsons,sizeof(jsons),"{\"%s\":%s}",
+ OSUtils::ztsnprintf(jsons,sizeof(jsons),"{\"%s\":%s}",
arg2.substr(0,eqidx).c_str(),
(((arg2.substr(eqidx,2) == "=t")||(arg2.substr(eqidx,2) == "=1")) ? "true" : "false"));
char cl[128];
- Utils::snprintf(cl,sizeof(cl),"%u",(unsigned int)strlen(jsons));
+ OSUtils::ztsnprintf(cl,sizeof(cl),"%u",(unsigned int)strlen(jsons));
requestHeaders["Content-Type"] = "application/json";
requestHeaders["Content-Length"] = cl;
unsigned int scode = Http::POST(
@@ -600,6 +628,48 @@ static int cli(int argc,char **argv)
cliPrintHelp(argv[0],stderr);
return 2;
}
+ } else if (command == "get") {
+ if (arg1.length() != 16) {
+ cliPrintHelp(argv[0],stderr);
+ return 2;
+ }
+ char jsons[1024], cl[128];
+ OSUtils::ztsnprintf(cl,sizeof(cl),"%u",(unsigned int)strlen(jsons));
+ requestHeaders["Content-Type"] = "application/json";
+ requestHeaders["Content-Length"] = cl;
+ const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/network",requestHeaders,responseHeaders,responseBody);
+
+ if (scode == 0) {
+ printf("Error connecting to the ZeroTier service: %s\n\nPlease check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S, responseBody.c_str());
+ return 1;
+ }
+
+ nlohmann::json j;
+ try {
+ j = OSUtils::jsonParse(responseBody);
+ } catch (std::exception &exc) {
+ printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what());
+ return 1;
+ } catch ( ... ) {
+ printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str());
+ return 1;
+ }
+ if (j.is_array()) {
+ for(unsigned long i=0;i<j.size();++i) {
+ nlohmann::json &n = j[i];
+ if (n.is_object()) {
+ if (n["id"] == arg1) {
+ printf("%s\n", OSUtils::jsonString(n[arg2],"-").c_str());
+ }
+ }
+ }
+ }
+ if (scode == 200) {
+ return 0;
+ } else {
+ printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
+ return 1;
+ }
} else {
cliPrintHelp(argv[0],stderr);
return 0;
@@ -640,7 +710,7 @@ static Identity getIdFromArg(char *arg)
} else { // identity is to be read from a file
std::string idser;
if (OSUtils::readFile(arg,idser)) {
- if (id.fromString(idser))
+ if (id.fromString(idser.c_str()))
return id;
}
}
@@ -681,14 +751,15 @@ static int idtool(int argc,char **argv)
}
}
- std::string idser = id.toString(true);
+ char idtmp[1024];
+ std::string idser = id.toString(true,idtmp);
if (argc >= 3) {
if (!OSUtils::writeFile(argv[2],idser)) {
fprintf(stderr,"Error writing to %s" ZT_EOL_S,argv[2]);
return 1;
} else printf("%s written" ZT_EOL_S,argv[2]);
if (argc >= 4) {
- idser = id.toString(false);
+ idser = id.toString(false,idtmp);
if (!OSUtils::writeFile(argv[3],idser)) {
fprintf(stderr,"Error writing to %s" ZT_EOL_S,argv[3]);
return 1;
@@ -723,7 +794,8 @@ static int idtool(int argc,char **argv)
return 1;
}
- printf("%s",id.toString(false).c_str());
+ char idtmp[1024];
+ printf("%s",id.toString(false,idtmp));
} else if (!strcmp(argv[1],"sign")) {
if (argc < 4) {
idtoolPrintHelp(stdout,argv[0]);
@@ -747,7 +819,8 @@ static int idtool(int argc,char **argv)
return 1;
}
C25519::Signature signature = id.sign(inf.data(),(unsigned int)inf.length());
- printf("%s",Utils::hex(signature.data,(unsigned int)signature.size()).c_str());
+ char hexbuf[1024];
+ printf("%s",Utils::hex(signature.data,ZT_C25519_SIGNATURE_LEN,hexbuf));
} else if (!strcmp(argv[1],"verify")) {
if (argc < 4) {
idtoolPrintHelp(stdout,argv[0]);
@@ -766,7 +839,8 @@ static int idtool(int argc,char **argv)
return 1;
}
- std::string signature(Utils::unhex(argv[4]));
+ char buf[4096];
+ std::string signature(buf,Utils::unhex(argv[4],buf,(unsigned int)sizeof(buf)));
if ((signature.length() > ZT_ADDRESS_LENGTH)&&(id.verify(inf.data(),(unsigned int)inf.length(),signature.data(),(unsigned int)signature.length()))) {
printf("%s signature valid" ZT_EOL_S,argv[3]);
} else {
@@ -785,14 +859,15 @@ static int idtool(int argc,char **argv)
C25519::Pair kp(C25519::generate());
+ char idtmp[4096];
nlohmann::json mj;
mj["objtype"] = "world";
mj["worldType"] = "moon";
- mj["updatesMustBeSignedBy"] = mj["signingKey"] = Utils::hex(kp.pub.data,(unsigned int)kp.pub.size());
- mj["signingKey_SECRET"] = Utils::hex(kp.priv.data,(unsigned int)kp.priv.size());
- mj["id"] = id.address().toString();
+ mj["updatesMustBeSignedBy"] = mj["signingKey"] = Utils::hex(kp.pub.data,ZT_C25519_PUBLIC_KEY_LEN,idtmp);
+ mj["signingKey_SECRET"] = Utils::hex(kp.priv.data,ZT_C25519_PRIVATE_KEY_LEN,idtmp);
+ mj["id"] = id.address().toString(idtmp);
nlohmann::json seedj;
- seedj["identity"] = id.toString(false);
+ seedj["identity"] = id.toString(false,idtmp);
seedj["stableEndpoints"] = nlohmann::json::array();
(mj["roots"] = nlohmann::json::array()).push_back(seedj);
std::string mjd(OSUtils::jsonDump(mj));
@@ -828,9 +903,9 @@ static int idtool(int argc,char **argv)
C25519::Pair signingKey;
C25519::Public updatesMustBeSignedBy;
- Utils::unhex(OSUtils::jsonString(mj["signingKey"],""),signingKey.pub.data,(unsigned int)signingKey.pub.size());
- Utils::unhex(OSUtils::jsonString(mj["signingKey_SECRET"],""),signingKey.priv.data,(unsigned int)signingKey.priv.size());
- Utils::unhex(OSUtils::jsonString(mj["updatesMustBeSignedBy"],""),updatesMustBeSignedBy.data,(unsigned int)updatesMustBeSignedBy.size());
+ Utils::unhex(OSUtils::jsonString(mj["signingKey"],"").c_str(),signingKey.pub.data,ZT_C25519_PUBLIC_KEY_LEN);
+ Utils::unhex(OSUtils::jsonString(mj["signingKey_SECRET"],"").c_str(),signingKey.priv.data,ZT_C25519_PRIVATE_KEY_LEN);
+ Utils::unhex(OSUtils::jsonString(mj["updatesMustBeSignedBy"],"").c_str(),updatesMustBeSignedBy.data,ZT_C25519_PUBLIC_KEY_LEN);
std::vector<World::Root> roots;
nlohmann::json &rootsj = mj["roots"];
@@ -839,11 +914,11 @@ static int idtool(int argc,char **argv)
nlohmann::json &r = rootsj[i];
if (r.is_object()) {
roots.push_back(World::Root());
- roots.back().identity = Identity(OSUtils::jsonString(r["identity"],""));
+ roots.back().identity = Identity(OSUtils::jsonString(r["identity"],"").c_str());
nlohmann::json &stableEndpointsj = r["stableEndpoints"];
if (stableEndpointsj.is_array()) {
for(unsigned long k=0;k<(unsigned long)stableEndpointsj.size();++k)
- roots.back().stableEndpoints.push_back(InetAddress(OSUtils::jsonString(stableEndpointsj[k],"")));
+ roots.back().stableEndpoints.push_back(InetAddress(OSUtils::jsonString(stableEndpointsj[k],"").c_str()));
std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end());
}
}
@@ -851,12 +926,12 @@ static int idtool(int argc,char **argv)
}
std::sort(roots.begin(),roots.end());
- const uint64_t now = OSUtils::now();
+ const int64_t now = OSUtils::now();
World w(World::make(t,id,now,updatesMustBeSignedBy,roots,signingKey));
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> wbuf;
w.serialize(wbuf);
char fn[128];
- Utils::snprintf(fn,sizeof(fn),"%.16llx.moon",w.id());
+ OSUtils::ztsnprintf(fn,sizeof(fn),"%.16llx.moon",w.id());
OSUtils::writeFile(fn,wbuf.data(),wbuf.size());
printf("wrote %s (signed world with timestamp %llu)" ZT_EOL_S,fn,(unsigned long long)now);
}
@@ -979,7 +1054,7 @@ static void dropPrivileges(const char *procName,const std::string &homeDir)
// Change ownership of our home directory if everything looks good (does nothing if already chown'd)
_recursiveChown(homeDir.c_str(),targetUser->pw_uid,targetUser->pw_gid);
- if (_setCapabilities((1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID) | (1 << CAP_SETGID)) < 0) {
+ if (_setCapabilities((1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID) | (1 << CAP_SETGID) | (1 << CAP_NET_BIND_SERVICE)) < 0) {
_notDropping(procName,homeDir);
return;
}
@@ -1003,7 +1078,7 @@ static void dropPrivileges(const char *procName,const std::string &homeDir)
exit(1);
}
- if (_setCapabilities((1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW)) < 0) {
+ if (_setCapabilities((1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_NET_BIND_SERVICE)) < 0) {
fprintf(stderr,"%s: FATAL: unable to drop capabilities after relinquishing root" ZT_EOL_S,procName);
exit(1);
}
@@ -1338,7 +1413,7 @@ int main(int argc,char **argv)
if (argv[i][2]) {
printHelp(argv[0],stdout);
return 0;
- } else return idtool(argc,argv);
+ } else return idtool(argc-1,argv+1);
case 'q': // Invoke cli personality
if (argv[i][2]) {
@@ -1480,7 +1555,8 @@ int main(int argc,char **argv)
#ifdef __UNIX_LIKE__
#ifdef ZT_HAVE_DROP_PRIVILEGES
- dropPrivileges(argv[0],homeDir);
+ if (!skipRootCheck)
+ dropPrivileges(argv[0],homeDir);
#endif
std::string pidPath(homeDir + ZT_PATH_SEPARATOR_S + ZT_PID_PATH);
diff --git a/osdep/Arp.cpp b/osdep/Arp.cpp
index fcc122f0..cc4e920a 100644
--- a/osdep/Arp.cpp
+++ b/osdep/Arp.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
diff --git a/osdep/Arp.hpp b/osdep/Arp.hpp
index 5f0d199a..27e92fdb 100644
--- a/osdep/Arp.hpp
+++ b/osdep/Arp.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_ARP_HPP
diff --git a/osdep/BSDEthernetTap.cpp b/osdep/BSDEthernetTap.cpp
index 62fabc48..053df21d 100644
--- a/osdep/BSDEthernetTap.cpp
+++ b/osdep/BSDEthernetTap.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdint.h>
@@ -86,9 +94,6 @@ BSDEthernetTap::BSDEthernetTap(
Mutex::Lock _gl(globalTapCreateLock);
- if (mtu > 2800)
- throw std::runtime_error("max tap MTU is 2800");
-
#ifdef __FreeBSD__
/* FreeBSD allows long interface names and interface renaming */
@@ -109,8 +114,8 @@ BSDEthernetTap::BSDEthernetTap(
std::vector<std::string> devFiles(OSUtils::listDirectory("/dev"));
for(int i=9993;i<(9993+128);++i) {
- Utils::snprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i);
- Utils::snprintf(devpath,sizeof(devpath),"/dev/%s",tmpdevname);
+ OSUtils::ztsnprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i);
+ OSUtils::ztsnprintf(devpath,sizeof(devpath),"/dev/%s",tmpdevname);
if (std::find(devFiles.begin(),devFiles.end(),std::string(tmpdevname)) == devFiles.end()) {
long cpid = (long)vfork();
if (cpid == 0) {
@@ -147,8 +152,8 @@ BSDEthernetTap::BSDEthernetTap(
/* Other BSDs like OpenBSD only have a limited number of tap devices that cannot be renamed */
for(int i=0;i<64;++i) {
- Utils::snprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i);
- Utils::snprintf(devpath,sizeof(devpath),"/dev/%s",tmpdevname);
+ OSUtils::ztsnprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i);
+ OSUtils::ztsnprintf(devpath,sizeof(devpath),"/dev/%s",tmpdevname);
_fd = ::open(devpath,O_RDWR);
if (_fd > 0) {
_dev = tmpdevname;
@@ -166,9 +171,9 @@ BSDEthernetTap::BSDEthernetTap(
}
// Configure MAC address and MTU, bring interface up
- Utils::snprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]);
- Utils::snprintf(mtustr,sizeof(mtustr),"%u",_mtu);
- Utils::snprintf(metstr,sizeof(metstr),"%u",_metric);
+ OSUtils::ztsnprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]);
+ OSUtils::ztsnprintf(mtustr,sizeof(mtustr),"%u",_mtu);
+ OSUtils::ztsnprintf(metstr,sizeof(metstr),"%u",_metric);
long cpid = (long)vfork();
if (cpid == 0) {
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"lladdr",ethaddr,"mtu",mtustr,"metric",metstr,"up",(const char *)0);
@@ -222,7 +227,8 @@ static bool ___removeIp(const std::string &_dev,const InetAddress &ip)
{
long cpid = (long)vfork();
if (cpid == 0) {
- execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"inet",ip.toIpString().c_str(),"-alias",(const char *)0);
+ char ipbuf[64];
+ execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"inet",ip.toIpString(ipbuf),"-alias",(const char *)0);
_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
@@ -251,7 +257,8 @@ bool BSDEthernetTap::addIp(const InetAddress &ip)
long cpid = (long)vfork();
if (cpid == 0) {
- ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),ip.isV4() ? "inet" : "inet6",ip.toString().c_str(),"alias",(const char *)0);
+ char tmp[128];
+ ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),ip.isV4() ? "inet" : "inet6",ip.toString(tmp),"alias",(const char *)0);
::_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
@@ -313,7 +320,7 @@ std::vector<InetAddress> BSDEthernetTap::ips() const
void BSDEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
{
- char putBuf[4096];
+ char putBuf[ZT_MAX_MTU + 64];
if ((_fd > 0)&&(len <= _mtu)&&(_enabled)) {
to.copyTo(putBuf,6);
from.copyTo(putBuf + 6,6);
@@ -373,49 +380,22 @@ void BSDEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,std:
_multicastGroups.swap(newGroups);
}
-/*
-bool BSDEthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
+void BSDEthernetTap::setMtu(unsigned int mtu)
{
- std::set<MulticastGroup> newGroups;
- struct ifmaddrs *ifmap = (struct ifmaddrs *)0;
- if (!getifmaddrs(&ifmap)) {
- struct ifmaddrs *p = ifmap;
- while (p) {
- if (p->ifma_addr->sa_family == AF_LINK) {
- struct sockaddr_dl *in = (struct sockaddr_dl *)p->ifma_name;
- struct sockaddr_dl *la = (struct sockaddr_dl *)p->ifma_addr;
- if ((la->sdl_alen == 6)&&(in->sdl_nlen <= _dev.length())&&(!memcmp(_dev.data(),in->sdl_data,in->sdl_nlen)))
- newGroups.insert(MulticastGroup(MAC(la->sdl_data + la->sdl_nlen,6),0));
- }
- p = p->ifma_next;
+ if (mtu != _mtu) {
+ _mtu = mtu;
+ long cpid = (long)vfork();
+ if (cpid == 0) {
+ char tmp[64];
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%u",mtu);
+ execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"mtu",tmp,(const char *)0);
+ _exit(-1);
+ } else if (cpid > 0) {
+ int exitcode = -1;
+ waitpid(cpid,&exitcode,0);
}
- freeifmaddrs(ifmap);
}
-
- {
- std::set<InetAddress> allIps(ips());
- for(std::set<InetAddress>::const_iterator i(allIps.begin());i!=allIps.end();++i)
- newGroups.insert(MulticastGroup::deriveMulticastGroupForAddressResolution(*i));
- }
-
- bool changed = false;
-
- for(std::set<MulticastGroup>::iterator mg(newGroups.begin());mg!=newGroups.end();++mg) {
- if (!groups.count(*mg)) {
- groups.insert(*mg);
- changed = true;
- }
- }
- for(std::set<MulticastGroup>::iterator mg(groups.begin());mg!=groups.end();) {
- if ((!newGroups.count(*mg))&&(*mg != _blindWildcardMulticastGroup)) {
- groups.erase(mg++);
- changed = true;
- } else ++mg;
- }
-
- return changed;
}
-*/
void BSDEthernetTap::threadMain()
throw()
@@ -423,7 +403,7 @@ void BSDEthernetTap::threadMain()
fd_set readfds,nullfds;
MAC to,from;
int n,nfds,r;
- char getBuf[8194];
+ char getBuf[ZT_MAX_MTU + 64];
// Wait for a moment after startup -- wait for Network to finish
// constructing itself.
diff --git a/osdep/BSDEthernetTap.hpp b/osdep/BSDEthernetTap.hpp
index 8c6314db..3d91dbbb 100644
--- a/osdep/BSDEthernetTap.hpp
+++ b/osdep/BSDEthernetTap.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_BSDETHERNETTAP_HPP
@@ -57,6 +65,7 @@ public:
std::string deviceName() const;
void setFriendlyName(const char *friendlyName);
void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
+ void setMtu(unsigned int mtu);
void threadMain()
throw();
diff --git a/osdep/Binder.hpp b/osdep/Binder.hpp
index 9829f170..93fad9f1 100644
--- a/osdep/Binder.hpp
+++ b/osdep/Binder.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_BINDER_HPP
@@ -49,8 +57,9 @@
#include <algorithm>
#include <utility>
#include <map>
+#include <set>
+#include <atomic>
-#include "../node/NonCopyable.hpp"
#include "../node/InetAddress.hpp"
#include "../node/Mutex.hpp"
#include "../node/Utils.hpp"
@@ -58,13 +67,12 @@
#include "Phy.hpp"
#include "OSUtils.hpp"
-/**
- * Period between binder rescans/refreshes
- *
- * OneService also does this on detected restarts.
- */
+// Period between refreshes of bindings
#define ZT_BINDER_REFRESH_PERIOD 30000
+// Max number of bindings
+#define ZT_BINDER_MAX_BINDINGS 256
+
namespace ZeroTier {
/**
@@ -78,29 +86,22 @@ namespace ZeroTier {
* On OSes that do not support local port enumeration or where this is not
* meaningful, this degrades to binding to wildcard.
*/
-class Binder : NonCopyable
+class Binder
{
private:
struct _Binding
{
- _Binding() :
- udpSock((PhySocket *)0),
- tcpListenSock((PhySocket *)0),
- address() {}
-
+ _Binding() : udpSock((PhySocket *)0),tcpListenSock((PhySocket *)0) {}
PhySocket *udpSock;
PhySocket *tcpListenSock;
InetAddress address;
};
public:
- Binder() {}
+ Binder() : _bindingCount(0) {}
/**
- * Close all bound ports
- *
- * This should be called on shutdown. It closes listen sockets and UDP ports
- * but not TCP connections from any TCP listen sockets.
+ * Close all bound ports, should be called on shutdown
*
* @param phy Physical interface
*/
@@ -108,10 +109,11 @@ public:
void closeAll(Phy<PHY_HANDLER_TYPE> &phy)
{
Mutex::Lock _l(_lock);
- for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) {
- phy.close(i->udpSock,false);
- phy.close(i->tcpListenSock,false);
+ for(unsigned int b=0,c=_bindingCount;b<c;++b) {
+ phy.close(_bindings[b].udpSock,false);
+ phy.close(_bindings[b].tcpListenSock,false);
}
+ _bindingCount = 0;
}
/**
@@ -121,325 +123,341 @@ public:
* changes, on startup, or periodically (e.g. every 30-60s).
*
* @param phy Physical interface
- * @param port Port to bind to on all interfaces (TCP and UDP)
- * @param ignoreInterfacesByName Ignore these interfaces by name
- * @param ignoreInterfacesByNamePrefix Ignore these interfaces by name-prefix (starts-with, e.g. zt ignores zt*)
- * @param ignoreInterfacesByAddress Ignore these interfaces by address
+ * @param ports Ports to bind on all interfaces
+ * @param portCount Number of ports
+ * @param explicitBind If present, override interface IP detection and bind to these (if possible)
+ * @param ifChecker Interface checker function to see if an interface should be used
* @tparam PHY_HANDLER_TYPE Type for Phy<> template
* @tparam INTERFACE_CHECKER Type for class containing shouldBindInterface() method
*/
template<typename PHY_HANDLER_TYPE,typename INTERFACE_CHECKER>
- void refresh(Phy<PHY_HANDLER_TYPE> &phy,unsigned int port,INTERFACE_CHECKER &ifChecker)
+ void refresh(Phy<PHY_HANDLER_TYPE> &phy,unsigned int *ports,unsigned int portCount,const std::vector<InetAddress> explicitBind,INTERFACE_CHECKER &ifChecker)
{
std::map<InetAddress,std::string> localIfAddrs;
- PhySocket *udps;
- //PhySocket *tcps;
+ PhySocket *udps,*tcps;
Mutex::Lock _l(_lock);
+ bool interfacesEnumerated = true;
+ if (explicitBind.empty()) {
#ifdef __WINDOWS__
- char aabuf[32768];
- ULONG aalen = sizeof(aabuf);
- if (GetAdaptersAddresses(AF_UNSPEC,GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_MULTICAST|GAA_FLAG_SKIP_DNS_SERVER,(void *)0,reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf),&aalen) == NO_ERROR) {
- PIP_ADAPTER_ADDRESSES a = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf);
- while (a) {
- PIP_ADAPTER_UNICAST_ADDRESS ua = a->FirstUnicastAddress;
- while (ua) {
- InetAddress ip(ua->Address.lpSockaddr);
- if (ifChecker.shouldBindInterface("",ip)) {
- switch(ip.ipScope()) {
- default: break;
- case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
- case InetAddress::IP_SCOPE_GLOBAL:
- case InetAddress::IP_SCOPE_SHARED:
- case InetAddress::IP_SCOPE_PRIVATE:
- ip.setPort(port);
- localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string()));
- break;
+ char aabuf[32768];
+ ULONG aalen = sizeof(aabuf);
+ if (GetAdaptersAddresses(AF_UNSPEC,GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_MULTICAST|GAA_FLAG_SKIP_DNS_SERVER,(void *)0,reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf),&aalen) == NO_ERROR) {
+ PIP_ADAPTER_ADDRESSES a = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf);
+ while (a) {
+ PIP_ADAPTER_UNICAST_ADDRESS ua = a->FirstUnicastAddress;
+ while (ua) {
+ InetAddress ip(ua->Address.lpSockaddr);
+ if (ifChecker.shouldBindInterface("",ip)) {
+ switch(ip.ipScope()) {
+ default: break;
+ case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
+ case InetAddress::IP_SCOPE_GLOBAL:
+ case InetAddress::IP_SCOPE_SHARED:
+ case InetAddress::IP_SCOPE_PRIVATE:
+ for(int x=0;x<(int)portCount;++x) {
+ ip.setPort(ports[x]);
+ localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string()));
+ }
+ break;
+ }
}
+ ua = ua->Next;
}
- ua = ua->Next;
+ a = a->Next;
}
- a = a->Next;
}
- }
+ else {
+ interfacesEnumerated = false;
+ }
#else // not __WINDOWS__
- /* On Linux we use an alternative method if available since getifaddrs()
- * gets very slow when there are lots of network namespaces. This won't
- * work unless /proc/PID/net/if_inet6 exists and it may not on some
- * embedded systems, so revert to getifaddrs() there. */
+ /* On Linux we use an alternative method if available since getifaddrs()
+ * gets very slow when there are lots of network namespaces. This won't
+ * work unless /proc/PID/net/if_inet6 exists and it may not on some
+ * embedded systems, so revert to getifaddrs() there. */
#ifdef __LINUX__
- char fn[256],tmp[256];
- std::set<std::string> ifnames;
- const unsigned long pid = (unsigned long)getpid();
-
- // Get all device names
- Utils::snprintf(fn,sizeof(fn),"/proc/%lu/net/dev",pid);
- FILE *procf = fopen(fn,"r");
- if (procf) {
- while (fgets(tmp,sizeof(tmp),procf)) {
- tmp[255] = 0;
- char *saveptr = (char *)0;
- for(char *f=Utils::stok(tmp," \t\r\n:|",&saveptr);(f);f=Utils::stok((char *)0," \t\r\n:|",&saveptr)) {
- if ((strcmp(f,"Inter-") != 0)&&(strcmp(f,"face") != 0)&&(f[0] != 0))
- ifnames.insert(f);
- break; // we only want the first field
+ char fn[256],tmp[256];
+ std::set<std::string> ifnames;
+ const unsigned long pid = (unsigned long)getpid();
+
+ // Get all device names
+ OSUtils::ztsnprintf(fn,sizeof(fn),"/proc/%lu/net/dev",pid);
+ FILE *procf = fopen(fn,"r");
+ if (procf) {
+ while (fgets(tmp,sizeof(tmp),procf)) {
+ tmp[255] = 0;
+ char *saveptr = (char *)0;
+ for(char *f=Utils::stok(tmp," \t\r\n:|",&saveptr);(f);f=Utils::stok((char *)0," \t\r\n:|",&saveptr)) {
+ if ((strcmp(f,"Inter-") != 0)&&(strcmp(f,"face") != 0)&&(f[0] != 0))
+ ifnames.insert(f);
+ break; // we only want the first field
+ }
}
+ fclose(procf);
+ }
+ else {
+ interfacesEnumerated = false;
}
- fclose(procf);
- }
- // Get IPv6 addresses (and any device names we don't already know)
- Utils::snprintf(fn,sizeof(fn),"/proc/%lu/net/if_inet6",pid);
- procf = fopen(fn,"r");
- if (procf) {
- while (fgets(tmp,sizeof(tmp),procf)) {
- tmp[255] = 0;
- char *saveptr = (char *)0;
- unsigned char ipbits[16];
- memset(ipbits,0,sizeof(ipbits));
- char *devname = (char *)0;
- int n = 0;
- for(char *f=Utils::stok(tmp," \t\r\n",&saveptr);(f);f=Utils::stok((char *)0," \t\r\n",&saveptr)) {
- switch(n++) {
- case 0: // IP in hex
- Utils::unhex(f,32,ipbits,16);
- break;
- case 5: // device name
- devname = f;
- break;
- }
- }
- if (devname) {
- ifnames.insert(devname);
- InetAddress ip(ipbits,16,0);
- if (ifChecker.shouldBindInterface(devname,ip)) {
- switch(ip.ipScope()) {
- default: break;
- case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
- case InetAddress::IP_SCOPE_GLOBAL:
- case InetAddress::IP_SCOPE_SHARED:
- case InetAddress::IP_SCOPE_PRIVATE:
- ip.setPort(port);
- localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(devname)));
+ // Get IPv6 addresses (and any device names we don't already know)
+ OSUtils::ztsnprintf(fn,sizeof(fn),"/proc/%lu/net/if_inet6",pid);
+ procf = fopen(fn,"r");
+ if (procf) {
+ while (fgets(tmp,sizeof(tmp),procf)) {
+ tmp[255] = 0;
+ char *saveptr = (char *)0;
+ unsigned char ipbits[16];
+ memset(ipbits,0,sizeof(ipbits));
+ char *devname = (char *)0;
+ int n = 0;
+ for(char *f=Utils::stok(tmp," \t\r\n",&saveptr);(f);f=Utils::stok((char *)0," \t\r\n",&saveptr)) {
+ switch(n++) {
+ case 0: // IP in hex
+ Utils::unhex(f,32,ipbits,16);
break;
+ case 5: // device name
+ devname = f;
+ break;
+ }
+ }
+ if (devname) {
+ ifnames.insert(devname);
+ InetAddress ip(ipbits,16,0);
+ if (ifChecker.shouldBindInterface(devname,ip)) {
+ switch(ip.ipScope()) {
+ default: break;
+ case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
+ case InetAddress::IP_SCOPE_GLOBAL:
+ case InetAddress::IP_SCOPE_SHARED:
+ case InetAddress::IP_SCOPE_PRIVATE:
+ for(int x=0;x<(int)portCount;++x) {
+ ip.setPort(ports[x]);
+ localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(devname)));
+ }
+ break;
+ }
}
}
}
+ fclose(procf);
}
- fclose(procf);
- }
-
- // Get IPv4 addresses for each device
- if (ifnames.size() > 0) {
- const int controlfd = (int)socket(AF_INET,SOCK_DGRAM,0);
- struct ifconf configuration;
- configuration.ifc_len = 0;
- configuration.ifc_buf = nullptr;
-
- if (controlfd < 0) goto ip4_address_error;
-
- if (ioctl(controlfd, SIOCGIFCONF, &configuration) < 0) goto ip4_address_error;
- configuration.ifc_buf = (char*)malloc(configuration.ifc_len);
-
- if (ioctl(controlfd, SIOCGIFCONF, &configuration) < 0) goto ip4_address_error;
-
- for (int i=0; i < (int)(configuration.ifc_len / sizeof(ifreq)); i ++) {
- struct ifreq& request = configuration.ifc_req[i];
- struct sockaddr* addr = &request.ifr_ifru.ifru_addr;
- if (addr->sa_family != AF_INET) continue;
- std::string ifname = request.ifr_ifrn.ifrn_name;
- // name can either be just interface name or interface name followed by ':' and arbitrary label
- if (ifname.find(':') != std::string::npos) {
- ifname = ifname.substr(0, ifname.find(':'));
- }
-
- InetAddress ip(&(((struct sockaddr_in *)addr)->sin_addr),4,0);
- if (ifChecker.shouldBindInterface(ifname.c_str(), ip)) {
- switch(ip.ipScope()) {
- default: break;
- case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
- case InetAddress::IP_SCOPE_GLOBAL:
- case InetAddress::IP_SCOPE_SHARED:
- case InetAddress::IP_SCOPE_PRIVATE:
- ip.setPort(port);
- localIfAddrs.insert(std::pair<InetAddress,std::string>(ip, ifname));
- break;
+ // Get IPv4 addresses for each device
+ if (ifnames.size() > 0) {
+ const int controlfd = (int)socket(AF_INET,SOCK_DGRAM,0);
+ struct ifconf configuration;
+ configuration.ifc_len = 0;
+ configuration.ifc_buf = nullptr;
+
+ if (controlfd < 0) goto ip4_address_error;
+ if (ioctl(controlfd, SIOCGIFCONF, &configuration) < 0) goto ip4_address_error;
+ configuration.ifc_buf = (char*)malloc(configuration.ifc_len);
+ if (ioctl(controlfd, SIOCGIFCONF, &configuration) < 0) goto ip4_address_error;
+
+ for (int i=0; i < (int)(configuration.ifc_len / sizeof(ifreq)); i ++) {
+ struct ifreq& request = configuration.ifc_req[i];
+ struct sockaddr* addr = &request.ifr_ifru.ifru_addr;
+ if (addr->sa_family != AF_INET) continue;
+ std::string ifname = request.ifr_ifrn.ifrn_name;
+ // name can either be just interface name or interface name followed by ':' and arbitrary label
+ if (ifname.find(':') != std::string::npos)
+ ifname = ifname.substr(0, ifname.find(':'));
+
+ InetAddress ip(&(((struct sockaddr_in *)addr)->sin_addr),4,0);
+ if (ifChecker.shouldBindInterface(ifname.c_str(), ip)) {
+ switch(ip.ipScope()) {
+ default: break;
+ case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
+ case InetAddress::IP_SCOPE_GLOBAL:
+ case InetAddress::IP_SCOPE_SHARED:
+ case InetAddress::IP_SCOPE_PRIVATE:
+ for(int x=0;x<(int)portCount;++x) {
+ ip.setPort(ports[x]);
+ localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,ifname));
+ }
+ break;
+ }
}
}
- }
- ip4_address_error:
- free(configuration.ifc_buf);
- if (controlfd > 0) close(controlfd);
- }
+ ip4_address_error:
+ free(configuration.ifc_buf);
+ if (controlfd > 0) close(controlfd);
+ }
- const bool gotViaProc = (localIfAddrs.size() > 0);
+ const bool gotViaProc = (localIfAddrs.size() > 0);
#else
- const bool gotViaProc = false;
+ const bool gotViaProc = false;
#endif
- if (!gotViaProc) {
- struct ifaddrs *ifatbl = (struct ifaddrs *)0;
- struct ifaddrs *ifa;
- if ((getifaddrs(&ifatbl) == 0)&&(ifatbl)) {
- ifa = ifatbl;
- while (ifa) {
- if ((ifa->ifa_name)&&(ifa->ifa_addr)) {
- InetAddress ip = *(ifa->ifa_addr);
- if (ifChecker.shouldBindInterface(ifa->ifa_name,ip)) {
- switch(ip.ipScope()) {
- default: break;
- case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
- case InetAddress::IP_SCOPE_GLOBAL:
- case InetAddress::IP_SCOPE_SHARED:
- case InetAddress::IP_SCOPE_PRIVATE:
- ip.setPort(port);
- localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(ifa->ifa_name)));
- break;
+ if (!gotViaProc) {
+ struct ifaddrs *ifatbl = (struct ifaddrs *)0;
+ struct ifaddrs *ifa;
+ if ((getifaddrs(&ifatbl) == 0)&&(ifatbl)) {
+ ifa = ifatbl;
+ while (ifa) {
+ if ((ifa->ifa_name)&&(ifa->ifa_addr)) {
+ InetAddress ip = *(ifa->ifa_addr);
+ if (ifChecker.shouldBindInterface(ifa->ifa_name,ip)) {
+ switch(ip.ipScope()) {
+ default: break;
+ case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
+ case InetAddress::IP_SCOPE_GLOBAL:
+ case InetAddress::IP_SCOPE_SHARED:
+ case InetAddress::IP_SCOPE_PRIVATE:
+ for(int x=0;x<(int)portCount;++x) {
+ ip.setPort(ports[x]);
+ localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(ifa->ifa_name)));
+ }
+ break;
+ }
}
}
+ ifa = ifa->ifa_next;
}
- ifa = ifa->ifa_next;
+ freeifaddrs(ifatbl);
+ }
+ else {
+ interfacesEnumerated = false;
}
- freeifaddrs(ifatbl);
}
- }
#endif
+ } else {
+ for(std::vector<InetAddress>::const_iterator i(explicitBind.begin());i!=explicitBind.end();++i)
+ localIfAddrs.insert(std::pair<InetAddress,std::string>(*i,std::string()));
+ }
// Default to binding to wildcard if we can't enumerate addresses
- if (localIfAddrs.empty()) {
- localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((uint32_t)0,port),std::string()));
- localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((const void *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16,port),std::string()));
+ if (!interfacesEnumerated && localIfAddrs.empty()) {
+ for(int x=0;x<(int)portCount;++x) {
+ localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((uint32_t)0,ports[x]),std::string()));
+ localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((const void *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16,ports[x]),std::string()));
+ }
}
- // Close any old bindings to anything that doesn't exist anymore
- for(typename std::vector<_Binding>::const_iterator bi(_bindings.begin());bi!=_bindings.end();++bi) {
- if (localIfAddrs.find(bi->address) == localIfAddrs.end()) {
- phy.close(bi->udpSock,false);
- phy.close(bi->tcpListenSock,false);
+ const unsigned int oldBindingCount = _bindingCount;
+ _bindingCount = 0;
+
+ // Save bindings that are still valid, close those that are not
+ for(unsigned int b=0;b<oldBindingCount;++b) {
+ if (localIfAddrs.find(_bindings[b].address) != localIfAddrs.end()) {
+ if (_bindingCount != b)
+ _bindings[(unsigned int)_bindingCount] = _bindings[b];
+ ++_bindingCount;
+ } else {
+ PhySocket *const udps = _bindings[b].udpSock;
+ PhySocket *const tcps = _bindings[b].tcpListenSock;
+ _bindings[b].udpSock = (PhySocket *)0;
+ _bindings[b].tcpListenSock = (PhySocket *)0;
+ phy.close(udps,false);
+ phy.close(tcps,false);
}
}
- std::vector<_Binding> newBindings;
+ // Create new bindings for those not already bound
for(std::map<InetAddress,std::string>::const_iterator ii(localIfAddrs.begin());ii!=localIfAddrs.end();++ii) {
- typename std::vector<_Binding>::const_iterator bi(_bindings.begin());
- while (bi != _bindings.end()) {
- if (bi->address == ii->first) {
- newBindings.push_back(*bi);
+ unsigned int bi = 0;
+ while (bi != _bindingCount) {
+ if (_bindings[bi].address == ii->first)
break;
- }
++bi;
}
-
- if (bi == _bindings.end()) {
+ if (bi == _bindingCount) {
udps = phy.udpBind(reinterpret_cast<const struct sockaddr *>(&(ii->first)),(void *)0,ZT_UDP_DESIRED_BUF_SIZE);
- if (udps) {
- //tcps = phy.tcpListen(reinterpret_cast<const struct sockaddr *>(&ii),(void *)0);
- //if (tcps) {
+ tcps = phy.tcpListen(reinterpret_cast<const struct sockaddr *>(&(ii->first)),(void *)0);
+ if ((udps)&&(tcps)) {
#ifdef __LINUX__
- // Bind Linux sockets to their device so routes tha we manage do not override physical routes (wish all platforms had this!)
- if (ii->second.length() > 0) {
- int fd = (int)Phy<PHY_HANDLER_TYPE>::getDescriptor(udps);
- char tmp[256];
- Utils::scopy(tmp,sizeof(tmp),ii->second.c_str());
- if (fd >= 0) {
- if (setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,tmp,strlen(tmp)) != 0) {
- fprintf(stderr,"WARNING: unable to set SO_BINDTODEVICE to bind %s to %s\n",ii->first.toIpString().c_str(),ii->second.c_str());
- }
- }
- }
+ // Bind Linux sockets to their device so routes tha we manage do not override physical routes (wish all platforms had this!)
+ if (ii->second.length() > 0) {
+ char tmp[256];
+ Utils::scopy(tmp,sizeof(tmp),ii->second.c_str());
+ int fd = (int)Phy<PHY_HANDLER_TYPE>::getDescriptor(udps);
+ if (fd >= 0)
+ setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,tmp,strlen(tmp));
+ fd = (int)Phy<PHY_HANDLER_TYPE>::getDescriptor(tcps);
+ if (fd >= 0)
+ setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,tmp,strlen(tmp));
+ }
#endif // __LINUX__
- newBindings.push_back(_Binding());
- newBindings.back().udpSock = udps;
- //newBindings.back().tcpListenSock = tcps;
- newBindings.back().address = ii->first;
- //} else {
- // phy.close(udps,false);
- //}
+ if (_bindingCount < ZT_BINDER_MAX_BINDINGS) {
+ _bindings[_bindingCount].udpSock = udps;
+ _bindings[_bindingCount].tcpListenSock = tcps;
+ _bindings[_bindingCount].address = ii->first;
+ ++_bindingCount;
+ }
+ } else {
+ phy.close(udps,false);
+ phy.close(tcps,false);
}
}
}
+ }
- // Swapping pointers and then letting the old one fall out of scope is faster than copying again
- _bindings.swap(newBindings);
+ /**
+ * @return All currently bound local interface addresses
+ */
+ inline std::vector<InetAddress> allBoundLocalInterfaceAddresses() const
+ {
+ std::vector<InetAddress> aa;
+ Mutex::Lock _l(_lock);
+ for(unsigned int b=0,c=_bindingCount;b<c;++b)
+ aa.push_back(_bindings[b].address);
+ return aa;
}
/**
- * Send a UDP packet from the specified local interface, or all
- *
- * Unfortunately even by examining the routing table there is no ultimately
- * robust way to tell where we might reach another host that works in all
- * environments. As a result, we send packets with null (wildcard) local
- * addresses from *every* bound interface.
- *
- * These are typically initial HELLOs, path probes, etc., since normal
- * conversations will have a local endpoint address. So the cost is low and
- * if the peer is not reachable via that route then the packet will go
- * nowhere and nothing will happen.
- *
- * It will of course only send via interface bindings of the same socket
- * family. No point in sending V4 via V6 or vice versa.
- *
- * In any case on most hosts there's only one or two interfaces that we
- * will use, so none of this is particularly costly.
- *
- * @param local Local interface address or null address for 'all'
- * @param remote Remote address
- * @param data Data to send
- * @param len Length of data
- * @param v4ttl If non-zero, send this packet with the specified IP TTL (IPv4 only)
+ * Send from all bound UDP sockets
*/
template<typename PHY_HANDLER_TYPE>
- inline bool udpSend(Phy<PHY_HANDLER_TYPE> &phy,const InetAddress &local,const InetAddress &remote,const void *data,unsigned int len,unsigned int v4ttl = 0) const
+ inline bool udpSendAll(Phy<PHY_HANDLER_TYPE> &phy,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl)
{
+ bool r = false;
Mutex::Lock _l(_lock);
- if (local) {
- for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) {
- if (i->address == local) {
- if ((v4ttl)&&(local.ss_family == AF_INET))
- phy.setIp4UdpTtl(i->udpSock,v4ttl);
- const bool result = phy.udpSend(i->udpSock,reinterpret_cast<const struct sockaddr *>(&remote),data,len);
- if ((v4ttl)&&(local.ss_family == AF_INET))
- phy.setIp4UdpTtl(i->udpSock,255);
- return result;
- }
- }
- return false;
- } else {
- bool result = false;
- for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) {
- if (i->address.ss_family == remote.ss_family) {
- if ((v4ttl)&&(remote.ss_family == AF_INET))
- phy.setIp4UdpTtl(i->udpSock,v4ttl);
- result |= phy.udpSend(i->udpSock,reinterpret_cast<const struct sockaddr *>(&remote),data,len);
- if ((v4ttl)&&(remote.ss_family == AF_INET))
- phy.setIp4UdpTtl(i->udpSock,255);
- }
- }
- return result;
+ for(unsigned int b=0,c=_bindingCount;b<c;++b) {
+ if (ttl) phy.setIp4UdpTtl(_bindings[b].udpSock,ttl);
+ if (phy.udpSend(_bindings[b].udpSock,(const struct sockaddr *)addr,data,len)) r = true;
+ if (ttl) phy.setIp4UdpTtl(_bindings[b].udpSock,255);
}
+ return r;
}
/**
- * @return All currently bound local interface addresses
+ * @param addr Address to check
+ * @return True if this is a bound local interface address
*/
- inline std::vector<InetAddress> allBoundLocalInterfaceAddresses()
+ inline bool isBoundLocalInterfaceAddress(const InetAddress &addr) const
{
Mutex::Lock _l(_lock);
- std::vector<InetAddress> aa;
- for(std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i)
- aa.push_back(i->address);
- return aa;
+ for(unsigned int b=0;b<_bindingCount;++b) {
+ if (_bindings[b].address == addr)
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Quickly check that a UDP socket is valid
+ *
+ * @param udpSock UDP socket to check
+ * @return True if socket is currently bound/allocated
+ */
+ inline bool isUdpSocketValid(PhySocket *const udpSock)
+ {
+ for(unsigned int b=0,c=_bindingCount;b<c;++b) {
+ if (_bindings[b].udpSock == udpSock)
+ return (b < _bindingCount); // double check atomic which may have changed
+ }
+ return false;
}
private:
- std::vector<_Binding> _bindings;
+ _Binding _bindings[ZT_BINDER_MAX_BINDINGS];
+ std::atomic<unsigned int> _bindingCount;
Mutex _lock;
};
diff --git a/osdep/BlockingQueue.hpp b/osdep/BlockingQueue.hpp
index 6172f4da..351a095a 100644
--- a/osdep/BlockingQueue.hpp
+++ b/osdep/BlockingQueue.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_BLOCKINGQUEUE_HPP
@@ -22,6 +30,7 @@
#include <queue>
#include <mutex>
#include <condition_variable>
+#include <chrono>
namespace ZeroTier {
@@ -34,7 +43,7 @@ template <class T>
class BlockingQueue
{
public:
- BlockingQueue(void) {}
+ BlockingQueue(void) : r(true) {}
inline void post(T t)
{
@@ -43,19 +52,53 @@ public:
c.notify_one();
}
- inline T get(void)
+ inline void stop(void)
+ {
+ std::lock_guard<std::mutex> lock(m);
+ r = false;
+ c.notify_all();
+ }
+
+ inline bool get(T &value)
{
std::unique_lock<std::mutex> lock(m);
- while(q.empty())
+ if (!r) return false;
+ while (q.empty()) {
c.wait(lock);
- T val = q.front();
+ if (!r) return false;
+ }
+ value = q.front();
+ q.pop();
+ return true;
+ }
+
+ enum TimedWaitResult
+ {
+ OK,
+ TIMED_OUT,
+ STOP
+ };
+
+ inline TimedWaitResult get(T &value,const unsigned long ms)
+ {
+ const std::chrono::milliseconds ms2{ms};
+ std::unique_lock<std::mutex> lock(m);
+ if (!r) return STOP;
+ while (q.empty()) {
+ if (c.wait_for(lock,ms2) == std::cv_status::timeout)
+ return ((r) ? TIMED_OUT : STOP);
+ else if (!r)
+ return STOP;
+ }
+ value = q.front();
q.pop();
- return val;
+ return OK;
}
private:
+ volatile bool r;
std::queue<T> q;
- mutable std::mutex m;
+ std::mutex m;
std::condition_variable c;
};
diff --git a/osdep/Http.cpp b/osdep/Http.cpp
index 064ccd0c..16785c96 100644
--- a/osdep/Http.cpp
+++ b/osdep/Http.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -104,12 +112,12 @@ struct HttpPhyHandler
inline void phyOnTcpWritable(PhySocket *sock,void **uptr)
{
- if (writePtr < writeSize) {
- long n = phy->streamSend(sock,writeBuf + writePtr,writeSize - writePtr,true);
+ if (writePtr < (unsigned long)writeBuf.length()) {
+ long n = phy->streamSend(sock,writeBuf.data() + writePtr,(unsigned long)writeBuf.length() - writePtr,true);
if (n > 0)
writePtr += n;
}
- if (writePtr >= writeSize)
+ if (writePtr >= (unsigned long)writeBuf.length())
phy->setNotifyWritable(sock,false);
}
@@ -127,8 +135,7 @@ struct HttpPhyHandler
unsigned long messageSize;
unsigned long writePtr;
uint64_t lastActivity;
- unsigned long writeSize;
- char writeBuf[32768];
+ std::string writeBuf;
unsigned long maxResponseSize;
std::map<std::string,std::string> *responseHeaders;
@@ -236,24 +243,26 @@ unsigned int Http::_do(
handler.lastActivity = OSUtils::now();
try {
- handler.writeSize = Utils::snprintf(handler.writeBuf,sizeof(handler.writeBuf),"%s %s HTTP/1.1\r\n",method,path);
- for(std::map<std::string,std::string>::const_iterator h(requestHeaders.begin());h!=requestHeaders.end();++h)
- handler.writeSize += Utils::snprintf(handler.writeBuf + handler.writeSize,sizeof(handler.writeBuf) - handler.writeSize,"%s: %s\r\n",h->first.c_str(),h->second.c_str());
- handler.writeSize += Utils::snprintf(handler.writeBuf + handler.writeSize,sizeof(handler.writeBuf) - handler.writeSize,"\r\n");
- if ((requestBody)&&(requestBodyLength)) {
- if ((handler.writeSize + requestBodyLength) > sizeof(handler.writeBuf)) {
- responseBody = "request too large";
- return 0;
- }
- memcpy(handler.writeBuf + handler.writeSize,requestBody,requestBodyLength);
- handler.writeSize += requestBodyLength;
+ char tmp[1024];
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%s %s HTTP/1.1\r\n",method,path);
+ handler.writeBuf.append(tmp);
+ for(std::map<std::string,std::string>::const_iterator h(requestHeaders.begin());h!=requestHeaders.end();++h) {
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%s: %s\r\n",h->first.c_str(),h->second.c_str());
+ handler.writeBuf.append(tmp);
}
+ handler.writeBuf.append("\r\n");
+ if ((requestBody)&&(requestBodyLength))
+ handler.writeBuf.append((const char *)requestBody,requestBodyLength);
} catch ( ... ) {
responseBody = "request too large";
return 0;
}
- handler.maxResponseSize = maxResponseSize;
+ if (maxResponseSize) {
+ handler.maxResponseSize = maxResponseSize;
+ } else {
+ handler.maxResponseSize = 2147483647;
+ }
handler.responseHeaders = &responseHeaders;
handler.responseBody = &responseBody;
handler.error = false;
diff --git a/osdep/Http.hpp b/osdep/Http.hpp
index e7d4d03e..b717c5c9 100644
--- a/osdep/Http.hpp
+++ b/osdep/Http.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_HTTP_HPP
diff --git a/osdep/LinuxEthernetTap.cpp b/osdep/LinuxEthernetTap.cpp
index f74efc0a..06bbbada 100644
--- a/osdep/LinuxEthernetTap.cpp
+++ b/osdep/LinuxEthernetTap.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdint.h>
@@ -40,6 +48,7 @@
#include <algorithm>
#include <utility>
+#include <string>
#include "../node/Constants.hpp"
#include "../node/Utils.hpp"
@@ -55,6 +64,19 @@ namespace ZeroTier {
static Mutex __tapCreateLock;
+static const char _base32_chars[32] = { 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','2','3','4','5','6','7' };
+static void _base32_5_to_8(const uint8_t *in,char *out)
+{
+ out[0] = _base32_chars[(in[0]) >> 3];
+ out[1] = _base32_chars[(in[0] & 0x07) << 2 | (in[1] & 0xc0) >> 6];
+ out[2] = _base32_chars[(in[1] & 0x3e) >> 1];
+ out[3] = _base32_chars[(in[1] & 0x01) << 4 | (in[2] & 0xf0) >> 4];
+ out[4] = _base32_chars[(in[2] & 0x0f) << 1 | (in[3] & 0x80) >> 7];
+ out[5] = _base32_chars[(in[3] & 0x7c) >> 2];
+ out[6] = _base32_chars[(in[3] & 0x03) << 3 | (in[4] & 0xe0) >> 5];
+ out[7] = _base32_chars[(in[4] & 0x1f)];
+}
+
LinuxEthernetTap::LinuxEthernetTap(
const char *homePath,
const MAC &mac,
@@ -75,13 +97,10 @@ LinuxEthernetTap::LinuxEthernetTap(
char procpath[128],nwids[32];
struct stat sbuf;
- Utils::snprintf(nwids,sizeof(nwids),"%.16llx",nwid);
+ OSUtils::ztsnprintf(nwids,sizeof(nwids),"%.16llx",nwid);
Mutex::Lock _l(__tapCreateLock); // create only one tap at a time, globally
- if (mtu > 2800)
- throw std::runtime_error("max tap MTU is 2800");
-
_fd = ::open("/dev/net/tun",O_RDWR);
if (_fd <= 0) {
_fd = ::open("/dev/tun",O_RDWR);
@@ -92,7 +111,7 @@ LinuxEthernetTap::LinuxEthernetTap(
struct ifreq ifr;
memset(&ifr,0,sizeof(ifr));
- // Try to recall our last device name, or pick an unused one if that fails.
+ // Restore device names from legacy devicemap, but for new devices we use a base32-based canonical naming
std::map<std::string,std::string> globalDeviceMap;
FILE *devmapf = fopen((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),"r");
if (devmapf) {
@@ -115,20 +134,36 @@ LinuxEthernetTap::LinuxEthernetTap(
std::map<std::string,std::string>::const_iterator gdmEntry = globalDeviceMap.find(nwids);
if (gdmEntry != globalDeviceMap.end()) {
Utils::scopy(ifr.ifr_name,sizeof(ifr.ifr_name),gdmEntry->second.c_str());
- Utils::snprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
+ OSUtils::ztsnprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
recalledDevice = (stat(procpath,&sbuf) != 0);
}
+
if (!recalledDevice) {
- int devno = 0;
- do {
#ifdef __SYNOLOGY__
- devno+=50; // Arbitrary number to prevent interface name conflicts
- Utils::snprintf(ifr.ifr_name,sizeof(ifr.ifr_name),"eth%d",devno++);
+ int devno = 50;
+ do {
+ OSUtils::ztsnprintf(ifr.ifr_name,sizeof(ifr.ifr_name),"eth%d",devno++);
+ OSUtils::ztsnprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
+ } while (stat(procpath,&sbuf) == 0); // try zt#++ until we find one that does not exist
#else
- Utils::snprintf(ifr.ifr_name,sizeof(ifr.ifr_name),"zt%d",devno++);
+ uint64_t trial = 0; // incremented in the very unlikely event of a name collision with another network
+ do {
+ const uint64_t nwid40 = (nwid ^ (nwid >> 24)) + trial++;
+ uint8_t tmp2[5];
+ char tmp3[11];
+ tmp2[0] = (uint8_t)((nwid40 >> 32) & 0xff);
+ tmp2[1] = (uint8_t)((nwid40 >> 24) & 0xff);
+ tmp2[2] = (uint8_t)((nwid40 >> 16) & 0xff);
+ tmp2[3] = (uint8_t)((nwid40 >> 8) & 0xff);
+ tmp2[4] = (uint8_t)(nwid40 & 0xff);
+ tmp3[0] = 'z';
+ tmp3[1] = 't';
+ _base32_5_to_8(tmp2,tmp3 + 2);
+ tmp3[10] = (char)0;
+ memcpy(ifr.ifr_name,tmp3,11);
+ OSUtils::ztsnprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
+ } while (stat(procpath,&sbuf) == 0);
#endif
- Utils::snprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
- } while (stat(procpath,&sbuf) == 0); // try zt#++ until we find one that does not exist
}
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
@@ -191,6 +226,7 @@ LinuxEthernetTap::LinuxEthernetTap(
(void)::pipe(_shutdownSignalPipe);
+ /*
globalDeviceMap[nwids] = _dev;
devmapf = fopen((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),"w");
if (devmapf) {
@@ -201,6 +237,7 @@ LinuxEthernetTap::LinuxEthernetTap(
}
fclose(devmapf);
}
+ */
_thread = Thread::start(this);
}
@@ -230,7 +267,8 @@ static bool ___removeIp(const std::string &_dev,const InetAddress &ip)
if (cpid == 0) {
OSUtils::redirectUnixOutputs("/dev/null",(const char *)0);
setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin", 1);
- ::execlp("ip","ip","addr","del",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0);
+ char iptmp[128];
+ ::execlp("ip","ip","addr","del",ip.toString(iptmp),"dev",_dev.c_str(),(const char *)0);
::_exit(-1);
} else {
int exitcode = -1;
@@ -251,7 +289,7 @@ bool LinuxEthernetTap::addIpSyn(std::vector<InetAddress> ips)
if (cpid == 0) {
OSUtils::redirectUnixOutputs("/dev/null",(const char *)0);
setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin", 1);
- // We must know if there is at least (one) of each protocol version so we
+ // We must know if there is at least (one) of each protocol version so we
// can properly enumerate address/netmask combinations in the ifcfg-dev file
for(int i=0; i<(int)ips.size(); i++) {
if (ips[i].isV4())
@@ -262,25 +300,28 @@ bool LinuxEthernetTap::addIpSyn(std::vector<InetAddress> ips)
// Assemble and write contents of ifcfg-dev file
for(int i=0; i<(int)ips.size(); i++) {
if (ips[i].isV4()) {
+ char iptmp[64],iptmp2[64];
std::string numstr4 = ip4_tot > 1 ? std::to_string(ip4) : "";
- cfg_contents += "\nIPADDR"+numstr4+"="+ips[i].toIpString()
- + "\nNETMASK"+numstr4+"="+ips[i].netmask().toIpString()+"\n";
+ cfg_contents += "\nIPADDR"+numstr4+"="+ips[i].toIpString(iptmp)
+ + "\nNETMASK"+numstr4+"="+ips[i].netmask().toIpString(iptmp2)+"\n";
ip4++;
}
else {
+ char iptmp[64],iptmp2[64];
std::string numstr6 = ip6_tot > 1 ? std::to_string(ip6) : "";
- cfg_contents += "\nIPV6ADDR"+numstr6+"="+ips[i].toIpString()
- + "\nNETMASK"+numstr6+"="+ips[i].netmask().toIpString()+"\n";
+ cfg_contents += "\nIPV6ADDR"+numstr6+"="+ips[i].toIpString(iptmp)
+ + "\nNETMASK"+numstr6+"="+ips[i].netmask().toIpString(iptmp2)+"\n";
ip6++;
}
}
OSUtils::writeFile(filepath.c_str(), cfg_contents.c_str(), cfg_contents.length());
// Finaly, add IPs
for(int i=0; i<(int)ips.size(); i++){
+ char iptmp[128],iptmp2[128];
if (ips[i].isV4())
- ::execlp("ip","ip","addr","add",ips[i].toString().c_str(),"broadcast",ips[i].broadcast().toIpString().c_str(),"dev",_dev.c_str(),(const char *)0);
+ ::execlp("ip","ip","addr","add",ips[i].toString(iptmp),"broadcast",ips[i].broadcast().toIpString(iptmp2),"dev",_dev.c_str(),(const char *)0);
else
- ::execlp("ip","ip","addr","add",ips[i].toString().c_str(),"dev",_dev.c_str(),(const char *)0);
+ ::execlp("ip","ip","addr","add",ips[i].toString(iptmp),"dev",_dev.c_str(),(const char *)0);
}
::_exit(-1);
} else if (cpid > 0) {
@@ -311,10 +352,11 @@ bool LinuxEthernetTap::addIp(const InetAddress &ip)
if (cpid == 0) {
OSUtils::redirectUnixOutputs("/dev/null",(const char *)0);
setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin", 1);
+ char iptmp[128],iptmp2[128];
if (ip.isV4()) {
- ::execlp("ip","ip","addr","add",ip.toString().c_str(),"broadcast",ip.broadcast().toIpString().c_str(),"dev",_dev.c_str(),(const char *)0);
+ ::execlp("ip","ip","addr","add",ip.toString(iptmp),"broadcast",ip.broadcast().toIpString(iptmp2),"dev",_dev.c_str(),(const char *)0);
} else {
- ::execlp("ip","ip","addr","add",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0);
+ ::execlp("ip","ip","addr","add",ip.toString(iptmp),"dev",_dev.c_str(),(const char *)0);
}
::_exit(-1);
} else if (cpid > 0) {
@@ -378,7 +420,7 @@ std::vector<InetAddress> LinuxEthernetTap::ips() const
void LinuxEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
{
- char putBuf[8194];
+ char putBuf[ZT_MAX_MTU + 64];
if ((_fd > 0)&&(len <= _mtu)&&(_enabled)) {
to.copyTo(putBuf,6);
from.copyTo(putBuf + 6,6);
@@ -447,13 +489,28 @@ void LinuxEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,st
_multicastGroups.swap(newGroups);
}
+void LinuxEthernetTap::setMtu(unsigned int mtu)
+{
+ if (_mtu != mtu) {
+ _mtu = mtu;
+ int sock = socket(AF_INET,SOCK_DGRAM,0);
+ if (sock > 0) {
+ struct ifreq ifr;
+ memset(&ifr,0,sizeof(ifr));
+ ifr.ifr_ifru.ifru_mtu = (int)mtu;
+ ioctl(sock,SIOCSIFMTU,(void *)&ifr);
+ close(sock);
+ }
+ }
+}
+
void LinuxEthernetTap::threadMain()
throw()
{
fd_set readfds,nullfds;
MAC to,from;
int n,nfds,r;
- char getBuf[8194];
+ char getBuf[ZT_MAX_MTU + 64];
Thread::sleep(500);
diff --git a/osdep/LinuxEthernetTap.hpp b/osdep/LinuxEthernetTap.hpp
index a2a00a79..5142eec1 100644
--- a/osdep/LinuxEthernetTap.hpp
+++ b/osdep/LinuxEthernetTap.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_LINUXETHERNETTAP_HPP
@@ -61,6 +69,7 @@ public:
std::string deviceName() const;
void setFriendlyName(const char *friendlyName);
void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
+ void setMtu(unsigned int mtu);
void threadMain()
throw();
diff --git a/osdep/ManagedRoute.cpp b/osdep/ManagedRoute.cpp
index 3a020d61..d7c80704 100644
--- a/osdep/ManagedRoute.cpp
+++ b/osdep/ManagedRoute.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include "../node/Constants.hpp"
@@ -238,7 +246,6 @@ static std::vector<_RTE> _getRTEs(const InetAddress &target,bool contains)
static void _routeCmd(const char *op,const InetAddress &target,const InetAddress &via,const char *ifscope,const char *localInterface)
{
- //printf("route %s %s %s %s %s\n",op,target.toString().c_str(),(via) ? via.toString().c_str() : "(null)",(ifscope) ? ifscope : "(null)",(localInterface) ? localInterface : "(null)");
long p = (long)fork();
if (p > 0) {
int exitcode = -1;
@@ -246,17 +253,19 @@ static void _routeCmd(const char *op,const InetAddress &target,const InetAddress
} else if (p == 0) {
::close(STDOUT_FILENO);
::close(STDERR_FILENO);
+ char ttmp[64];
+ char iptmp[64];
if (via) {
if ((ifscope)&&(ifscope[0])) {
- ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,"-ifscope",ifscope,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString().c_str(),via.toIpString().c_str(),(const char *)0);
+ ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,"-ifscope",ifscope,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString(ttmp),via.toIpString(iptmp),(const char *)0);
} else {
- ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString().c_str(),via.toIpString().c_str(),(const char *)0);
+ ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString(ttmp),via.toIpString(iptmp),(const char *)0);
}
} else if ((localInterface)&&(localInterface[0])) {
if ((ifscope)&&(ifscope[0])) {
- ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,"-ifscope",ifscope,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString().c_str(),"-interface",localInterface,(const char *)0);
+ ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,"-ifscope",ifscope,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString(ttmp),"-interface",localInterface,(const char *)0);
} else {
- ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString().c_str(),"-interface",localInterface,(const char *)0);
+ ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString(ttmp),"-interface",localInterface,(const char *)0);
}
}
::_exit(-1);
@@ -277,12 +286,13 @@ static void _routeCmd(const char *op,const InetAddress &target,const InetAddress
} else if (p == 0) {
::close(STDOUT_FILENO);
::close(STDERR_FILENO);
+ char ipbuf[64],ipbuf2[64];
if (via) {
- ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString().c_str(),"via",via.toIpString().c_str(),(const char *)0);
- ::execl(ZT_LINUX_IP_COMMAND_2,ZT_LINUX_IP_COMMAND_2,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString().c_str(),"via",via.toIpString().c_str(),(const char *)0);
+ ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString(ipbuf),"via",via.toIpString(ipbuf2),(const char *)0);
+ ::execl(ZT_LINUX_IP_COMMAND_2,ZT_LINUX_IP_COMMAND_2,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString(ipbuf),"via",via.toIpString(ipbuf2),(const char *)0);
} else if ((localInterface)&&(localInterface[0])) {
- ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString().c_str(),"dev",localInterface,(const char *)0);
- ::execl(ZT_LINUX_IP_COMMAND_2,ZT_LINUX_IP_COMMAND_2,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString().c_str(),"dev",localInterface,(const char *)0);
+ ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString(ipbuf),"dev",localInterface,(const char *)0);
+ ::execl(ZT_LINUX_IP_COMMAND_2,ZT_LINUX_IP_COMMAND_2,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString(ipbuf),"dev",localInterface,(const char *)0);
}
::_exit(-1);
}
diff --git a/osdep/ManagedRoute.hpp b/osdep/ManagedRoute.hpp
index fd77a79a..779ad6a1 100644
--- a/osdep/ManagedRoute.hpp
+++ b/osdep/ManagedRoute.hpp
@@ -1,3 +1,29 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
#ifndef ZT_MANAGEDROUTE_HPP
#define ZT_MANAGEDROUTE_HPP
@@ -8,7 +34,6 @@
#include "../node/Utils.hpp"
#include "../node/SharedPtr.hpp"
#include "../node/AtomicCounter.hpp"
-#include "../node/NonCopyable.hpp"
#include <stdexcept>
#include <vector>
@@ -19,7 +44,7 @@ namespace ZeroTier {
/**
* A ZT-managed route that used C++ RAII semantics to automatically clean itself up on deallocate
*/
-class ManagedRoute : NonCopyable
+class ManagedRoute
{
friend class SharedPtr<ManagedRoute>;
@@ -65,6 +90,9 @@ public:
inline const char *device() const { return _device; }
private:
+ ManagedRoute(const ManagedRoute &) {}
+ inline ManagedRoute &operator=(const ManagedRoute &) { return *this; }
+
InetAddress _target;
InetAddress _via;
InetAddress _systemVia; // for route overrides
diff --git a/osdep/NeighborDiscovery.cpp b/osdep/NeighborDiscovery.cpp
index 4f636310..d9862f3d 100644
--- a/osdep/NeighborDiscovery.cpp
+++ b/osdep/NeighborDiscovery.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include "NeighborDiscovery.hpp"
@@ -24,7 +32,7 @@
#include <assert.h>
namespace ZeroTier {
-
+
uint16_t calc_checksum (uint16_t *addr, int len)
{
int count = len;
@@ -185,7 +193,7 @@ sockaddr_storage NeighborDiscovery::processIncomingND(const uint8_t *nd, unsigne
assert(sizeof(_neighbor_advertisement) == 32);
const uint64_t now = OSUtils::now();
- sockaddr_storage ip = ZT_SOCKADDR_NULL;
+ sockaddr_storage ip = {0};
if (len >= sizeof(_neighbor_solicitation) && nd[0] == 0x87) {
// respond to Neighbor Solicitation request for local address
diff --git a/osdep/NeighborDiscovery.hpp b/osdep/NeighborDiscovery.hpp
index 47831bda..59186289 100644
--- a/osdep/NeighborDiscovery.hpp
+++ b/osdep/NeighborDiscovery.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_NEIGHBORDISCOVERY_HPP
diff --git a/osdep/OSUtils.cpp b/osdep/OSUtils.cpp
index fd5efed0..cadd4e6b 100644
--- a/osdep/OSUtils.cpp
+++ b/osdep/OSUtils.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -49,6 +57,23 @@
namespace ZeroTier {
+unsigned int OSUtils::ztsnprintf(char *buf,unsigned int len,const char *fmt,...)
+{
+ va_list ap;
+
+ va_start(ap,fmt);
+ int n = (int)vsnprintf(buf,len,fmt,ap);
+ va_end(ap);
+
+ if ((n >= (int)len)||(n < 0)) {
+ if (len)
+ buf[len - 1] = (char)0;
+ throw std::length_error("buf[] overflow");
+ }
+
+ return (unsigned int)n;
+}
+
#ifdef __UNIX_LIKE__
bool OSUtils::redirectUnixOutputs(const char *stdoutPath,const char *stderrPath)
throw()
@@ -108,7 +133,7 @@ std::vector<std::string> OSUtils::listDirectory(const char *path,bool includeDir
return r;
}
-long OSUtils::cleanDirectory(const char *path,const uint64_t olderThan)
+long OSUtils::cleanDirectory(const char *path,const int64_t olderThan)
{
long cleaned = 0;
@@ -125,8 +150,8 @@ long OSUtils::cleanDirectory(const char *path,const uint64_t olderThan)
date.LowPart = ffd.ftLastWriteTime.dwLowDateTime;
if (date.QuadPart > 0) {
date.QuadPart -= adjust.QuadPart;
- if ((uint64_t)((date.QuadPart / 10000000) * 1000) < olderThan) {
- Utils::snprintf(tmp, sizeof(tmp), "%s\\%s", path, ffd.cFileName);
+ if ((int64_t)((date.QuadPart / 10000000) * 1000) < olderThan) {
+ ztsnprintf(tmp, sizeof(tmp), "%s\\%s", path, ffd.cFileName);
if (DeleteFileA(tmp))
++cleaned;
}
@@ -149,9 +174,9 @@ long OSUtils::cleanDirectory(const char *path,const uint64_t olderThan)
break;
if (dptr) {
if ((strcmp(dptr->d_name,"."))&&(strcmp(dptr->d_name,".."))&&(dptr->d_type == DT_REG)) {
- Utils::snprintf(tmp,sizeof(tmp),"%s/%s",path,dptr->d_name);
+ ztsnprintf(tmp,sizeof(tmp),"%s/%s",path,dptr->d_name);
if (stat(tmp,&st) == 0) {
- uint64_t mt = (uint64_t)(st.st_mtime);
+ int64_t mt = (int64_t)(st.st_mtime);
if ((mt > 0)&&((mt * 1000) < olderThan)) {
if (unlink(tmp) == 0)
++cleaned;
@@ -279,7 +304,7 @@ int64_t OSUtils::getFileSize(const char *path)
bool OSUtils::readFile(const char *path,std::string &buf)
{
- char tmp[1024];
+ char tmp[16384];
FILE *f = fopen(path,"rb");
if (f) {
for(;;) {
@@ -355,6 +380,24 @@ std::vector<std::string> OSUtils::split(const char *s,const char *const sep,cons
std::string OSUtils::platformDefaultHomePath()
{
+#ifdef __QNAP__
+ char *cmd = "/sbin/getcfg zerotier Install_Path -f /etc/config/qpkg.conf";
+ char buf[128];
+ FILE *fp;
+ if ((fp = popen(cmd, "r")) == NULL) {
+ printf("Error opening pipe!\n");
+ return NULL;
+ }
+ while (fgets(buf, 128, fp) != NULL) { }
+ if(pclose(fp)) {
+ printf("Command not found or exited with error status\n");
+ return NULL;
+ }
+ std::string homeDir = std::string(buf);
+ homeDir.erase(std::remove(homeDir.begin(), homeDir.end(), '\n'), homeDir.end());
+ return homeDir;
+#endif
+
#ifdef __UNIX_LIKE__
#ifdef __APPLE__
@@ -391,7 +434,7 @@ std::string OSUtils::platformDefaultHomePath()
// Inline these massive JSON operations in one place only to reduce binary footprint and compile time
nlohmann::json OSUtils::jsonParse(const std::string &buf) { return nlohmann::json::parse(buf.c_str()); }
-std::string OSUtils::jsonDump(const nlohmann::json &j) { return j.dump(1); }
+std::string OSUtils::jsonDump(const nlohmann::json &j,int indentation) { return j.dump(indentation); }
uint64_t OSUtils::jsonInt(const nlohmann::json &jv,const uint64_t dfl)
{
@@ -408,6 +451,21 @@ uint64_t OSUtils::jsonInt(const nlohmann::json &jv,const uint64_t dfl)
return dfl;
}
+uint64_t OSUtils::jsonIntHex(const nlohmann::json &jv,const uint64_t dfl)
+{
+ try {
+ if (jv.is_number()) {
+ return (uint64_t)jv;
+ } else if (jv.is_string()) {
+ std::string s = jv;
+ return Utils::hexStrToU64(s.c_str());
+ } else if (jv.is_boolean()) {
+ return ((bool)jv ? 1ULL : 0ULL);
+ }
+ } catch ( ... ) {}
+ return dfl;
+}
+
bool OSUtils::jsonBool(const nlohmann::json &jv,const bool dfl)
{
try {
@@ -438,7 +496,7 @@ std::string OSUtils::jsonString(const nlohmann::json &jv,const char *dfl)
return jv;
} else if (jv.is_number()) {
char tmp[64];
- Utils::snprintf(tmp,sizeof(tmp),"%llu",(uint64_t)jv);
+ ztsnprintf(tmp,sizeof(tmp),"%llu",(uint64_t)jv);
return tmp;
} else if (jv.is_boolean()) {
return ((bool)jv ? std::string("1") : std::string("0"));
@@ -451,9 +509,10 @@ std::string OSUtils::jsonBinFromHex(const nlohmann::json &jv)
{
std::string s(jsonString(jv,""));
if (s.length() > 0) {
- char *buf = new char[(s.length() / 2) + 1];
+ unsigned int buflen = (unsigned int)((s.length() / 2) + 1);
+ char *buf = new char[buflen];
try {
- unsigned int l = Utils::unhex(s,buf,(unsigned int)s.length());
+ unsigned int l = Utils::unhex(s.c_str(),buf,buflen);
std::string b(buf,l);
delete [] buf;
return b;
diff --git a/osdep/OSUtils.hpp b/osdep/OSUtils.hpp
index b84d5d2d..324b7923 100644
--- a/osdep/OSUtils.hpp
+++ b/osdep/OSUtils.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_OSUTILS_HPP
@@ -25,7 +33,6 @@
#include <string.h>
#include <time.h>
-#include <string>
#include <stdexcept>
#include <vector>
#include <map>
@@ -43,6 +50,9 @@
#include <sys/time.h>
#include <sys/stat.h>
#include <arpa/inet.h>
+#ifdef __LINUX__
+#include <sys/syscall.h>
+#endif
#endif
#include "../ext/json/json.hpp"
@@ -55,6 +65,20 @@ namespace ZeroTier {
class OSUtils
{
public:
+ /**
+ * Variant of snprintf that is portable and throws an exception
+ *
+ * This just wraps the local implementation whatever it's called, while
+ * performing a few other checks and adding exceptions for overflow.
+ *
+ * @param buf Buffer to write to
+ * @param len Length of buffer in bytes
+ * @param fmt Format string
+ * @param ... Format arguments
+ * @throws std::length_error buf[] too short (buf[] will still be left null-terminated)
+ */
+ static unsigned int ztsnprintf(char *buf,unsigned int len,const char *fmt,...);
+
#ifdef __UNIX_LIKE__
/**
* Close STDOUT_FILENO and STDERR_FILENO and replace them with output to given path
@@ -77,7 +101,6 @@ public:
* @return True if delete was successful
*/
static inline bool rm(const char *path)
- throw()
{
#ifdef __WINDOWS__
return (DeleteFileA(path) != FALSE);
@@ -85,7 +108,7 @@ public:
return (unlink(path) == 0);
#endif
}
- static inline bool rm(const std::string &path) throw() { return rm(path.c_str()); }
+ static inline bool rm(const std::string &path) { return rm(path.c_str()); }
static inline bool mkdir(const char *path)
{
@@ -99,7 +122,17 @@ public:
return true;
#endif
}
- static inline bool mkdir(const std::string &path) throw() { return OSUtils::mkdir(path.c_str()); }
+ static inline bool mkdir(const std::string &path) { return OSUtils::mkdir(path.c_str()); }
+
+ static inline bool rename(const char *o,const char *n)
+ {
+#ifdef __WINDOWS__
+ DeleteFileA(n);
+ return (::rename(o,n) == 0);
+#else
+ return (::rename(o,n) == 0);
+#endif
+ }
/**
* List a directory's contents
@@ -118,7 +151,7 @@ public:
* @param olderThan Last modified older than timestamp (ms since epoch)
* @return Number of cleaned files or negative on fatal error
*/
- static long cleanDirectory(const char *path,const uint64_t olderThan);
+ static long cleanDirectory(const char *path,const int64_t olderThan);
/**
* Delete a directory and all its files and subdirectories recursively
@@ -176,8 +209,7 @@ public:
/**
* @return Current time in milliseconds since epoch
*/
- static inline uint64_t now()
- throw()
+ static inline int64_t now()
{
#ifdef __WINDOWS__
FILETIME ft;
@@ -187,35 +219,17 @@ public:
SystemTimeToFileTime(&st,&ft);
tmp.LowPart = ft.dwLowDateTime;
tmp.HighPart = ft.dwHighDateTime;
- return ( ((tmp.QuadPart - 116444736000000000ULL) / 10000L) + st.wMilliseconds );
+ return (int64_t)( ((tmp.QuadPart - 116444736000000000LL) / 10000L) + st.wMilliseconds );
#else
struct timeval tv;
- gettimeofday(&tv,(struct timezone *)0);
- return ( (1000ULL * (uint64_t)tv.tv_sec) + (uint64_t)(tv.tv_usec / 1000) );
-#endif
- };
-
- /**
- * @return Current time in seconds since epoch, to the highest available resolution
- */
- static inline double nowf()
- throw()
- {
-#ifdef __WINDOWS__
- FILETIME ft;
- SYSTEMTIME st;
- ULARGE_INTEGER tmp;
- GetSystemTime(&st);
- SystemTimeToFileTime(&st,&ft);
- tmp.LowPart = ft.dwLowDateTime;
- tmp.HighPart = ft.dwHighDateTime;
- return (((double)(tmp.QuadPart - 116444736000000000ULL)) / 10000000.0);
+#ifdef __LINUX__
+ syscall(SYS_gettimeofday,&tv,0); /* fix for musl libc broken gettimeofday bug */
#else
- struct timeval tv;
gettimeofday(&tv,(struct timezone *)0);
- return ( ((double)tv.tv_sec) + (((double)tv.tv_usec) / 1000000.0) );
#endif
- }
+ return ( (1000LL * (int64_t)tv.tv_sec) + (int64_t)(tv.tv_usec / 1000) );
+#endif
+ };
/**
* Read the full contents of a file into a string buffer
@@ -271,8 +285,9 @@ public:
static std::string platformDefaultHomePath();
static nlohmann::json jsonParse(const std::string &buf);
- static std::string jsonDump(const nlohmann::json &j);
+ static std::string jsonDump(const nlohmann::json &j,int indentation = 1);
static uint64_t jsonInt(const nlohmann::json &jv,const uint64_t dfl);
+ static uint64_t jsonIntHex(const nlohmann::json &jv,const uint64_t dfl);
static bool jsonBool(const nlohmann::json &jv,const bool dfl);
static std::string jsonString(const nlohmann::json &jv,const char *dfl);
static std::string jsonBinFromHex(const nlohmann::json &jv);
diff --git a/osdep/OSXEthernetTap.cpp b/osdep/OSXEthernetTap.cpp
index f70908b8..dbff6200 100644
--- a/osdep/OSXEthernetTap.cpp
+++ b/osdep/OSXEthernetTap.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdint.h>
@@ -328,10 +336,7 @@ OSXEthernetTap::OSXEthernetTap(
char devpath[64],ethaddr[64],mtustr[32],metstr[32],nwids[32];
struct stat stattmp;
- Utils::snprintf(nwids,sizeof(nwids),"%.16llx",nwid);
-
- if (mtu > 2800)
- throw std::runtime_error("max tap MTU is 2800");
+ OSUtils::ztsnprintf(nwids,sizeof(nwids),"%.16llx",nwid);
Mutex::Lock _gl(globalTapCreateLock);
@@ -386,13 +391,13 @@ OSXEthernetTap::OSXEthernetTap(
// Open the first unused tap device if we didn't recall a previous one.
if (!recalledDevice) {
for(int i=0;i<64;++i) {
- Utils::snprintf(devpath,sizeof(devpath),"/dev/zt%d",i);
+ OSUtils::ztsnprintf(devpath,sizeof(devpath),"/dev/zt%d",i);
if (stat(devpath,&stattmp))
throw std::runtime_error("no more TAP devices available");
_fd = ::open(devpath,O_RDWR);
if (_fd > 0) {
char foo[16];
- Utils::snprintf(foo,sizeof(foo),"zt%d",i);
+ OSUtils::ztsnprintf(foo,sizeof(foo),"zt%d",i);
_dev = foo;
break;
}
@@ -408,9 +413,9 @@ OSXEthernetTap::OSXEthernetTap(
}
// Configure MAC address and MTU, bring interface up
- Utils::snprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]);
- Utils::snprintf(mtustr,sizeof(mtustr),"%u",_mtu);
- Utils::snprintf(metstr,sizeof(metstr),"%u",_metric);
+ OSUtils::ztsnprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]);
+ OSUtils::ztsnprintf(mtustr,sizeof(mtustr),"%u",_mtu);
+ OSUtils::ztsnprintf(metstr,sizeof(metstr),"%u",_metric);
long cpid = (long)vfork();
if (cpid == 0) {
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"lladdr",ethaddr,"mtu",mtustr,"metric",metstr,"up",(const char *)0);
@@ -494,7 +499,8 @@ bool OSXEthernetTap::addIp(const InetAddress &ip)
long cpid = (long)vfork();
if (cpid == 0) {
- ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),(ip.ss_family == AF_INET6) ? "inet6" : "inet",ip.toString().c_str(),"alias",(const char *)0);
+ char tmp[128];
+ ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),(ip.ss_family == AF_INET6) ? "inet6" : "inet",ip.toString(tmp),"alias",(const char *)0);
::_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
@@ -514,7 +520,8 @@ bool OSXEthernetTap::removeIp(const InetAddress &ip)
if (*i == ip) {
long cpid = (long)vfork();
if (cpid == 0) {
- execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),(ip.ss_family == AF_INET6) ? "inet6" : "inet",ip.toIpString().c_str(),"-alias",(const char *)0);
+ char tmp[128];
+ execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),(ip.ss_family == AF_INET6) ? "inet6" : "inet",ip.toIpString(tmp),"-alias",(const char *)0);
_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
@@ -566,7 +573,7 @@ std::vector<InetAddress> OSXEthernetTap::ips() const
void OSXEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
{
- char putBuf[4096];
+ char putBuf[ZT_MAX_MTU + 64];
if ((_fd > 0)&&(len <= _mtu)&&(_enabled)) {
to.copyTo(putBuf,6);
from.copyTo(putBuf + 6,6);
@@ -624,13 +631,30 @@ void OSXEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,std:
_multicastGroups.swap(newGroups);
}
+void OSXEthernetTap::setMtu(unsigned int mtu)
+{
+ if (mtu != _mtu) {
+ _mtu = mtu;
+ long cpid = (long)vfork();
+ if (cpid == 0) {
+ char tmp[64];
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%u",mtu);
+ execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"mtu",tmp,(const char *)0);
+ _exit(-1);
+ } else if (cpid > 0) {
+ int exitcode = -1;
+ waitpid(cpid,&exitcode,0);
+ }
+ }
+}
+
void OSXEthernetTap::threadMain()
throw()
{
fd_set readfds,nullfds;
MAC to,from;
int n,nfds,r;
- char getBuf[8194];
+ char getBuf[ZT_MAX_MTU + 64];
Thread::sleep(500);
diff --git a/osdep/OSXEthernetTap.hpp b/osdep/OSXEthernetTap.hpp
index 5a96c210..fe402901 100644
--- a/osdep/OSXEthernetTap.hpp
+++ b/osdep/OSXEthernetTap.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_OSXETHERNETTAP_HPP
@@ -62,6 +70,7 @@ public:
std::string deviceName() const;
void setFriendlyName(const char *friendlyName);
void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
+ void setMtu(unsigned int mtu);
void threadMain()
throw();
diff --git a/osdep/Phy.hpp b/osdep/Phy.hpp
index eab8a317..e359ccdd 100644
--- a/osdep/Phy.hpp
+++ b/osdep/Phy.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_PHY_HPP
@@ -680,25 +688,26 @@ public:
* until one works.
*
* @param sock Socket
- * @param bufferSize Desired buffer sizes
+ * @param receiveBufferSize Desired size of receive buffer
+ * @param sendBufferSize Desired size of send buffer
*/
- inline void setBufferSizes(const PhySocket *sock,int bufferSize)
+ inline void setBufferSizes(const PhySocket *sock,int receiveBufferSize,int sendBufferSize)
{
PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock));
- if (bufferSize > 0) {
- int bs = bufferSize;
- while (bs >= 65536) {
- int tmpbs = bs;
+ if (receiveBufferSize > 0) {
+ while (receiveBufferSize > 0) {
+ int tmpbs = receiveBufferSize;
if (::setsockopt(sws.sock,SOL_SOCKET,SO_RCVBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0)
break;
- bs -= 16384;
+ receiveBufferSize -= 16384;
}
- bs = bufferSize;
- while (bs >= 65536) {
- int tmpbs = bs;
+ }
+ if (sendBufferSize > 0) {
+ while (sendBufferSize > 0) {
+ int tmpbs = sendBufferSize;
if (::setsockopt(sws.sock,SOL_SOCKET,SO_SNDBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0)
break;
- bs -= 16384;
+ sendBufferSize -= 16384;
}
}
}
@@ -807,7 +816,7 @@ public:
* @param sock Stream connection socket
* @param notifyWritable Want writable notifications?
*/
- inline const void setNotifyWritable(PhySocket *sock,bool notifyWritable)
+ inline void setNotifyWritable(PhySocket *sock,bool notifyWritable)
{
PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock));
if (notifyWritable) {
@@ -827,7 +836,7 @@ public:
* @param sock Socket to modify
* @param notifyReadable True if socket should be monitored for readability
*/
- inline const void setNotifyReadable(PhySocket *sock,bool notifyReadable)
+ inline void setNotifyReadable(PhySocket *sock,bool notifyReadable)
{
PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock));
if (notifyReadable) {
@@ -957,7 +966,7 @@ public:
case ZT_PHY_SOCKET_UDP:
if (FD_ISSET(s->sock,&rfds)) {
- for(;;) {
+ for(int k=0;k<1024;++k) {
memset(&ss,0,sizeof(ss));
socklen_t slen = sizeof(ss);
long n = (long)::recvfrom(s->sock,buf,sizeof(buf),0,(struct sockaddr *)&ss,&slen);
diff --git a/osdep/PortMapper.cpp b/osdep/PortMapper.cpp
index d3a19384..a7dd3046 100644
--- a/osdep/PortMapper.cpp
+++ b/osdep/PortMapper.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifdef ZT_USE_MINIUPNPC
@@ -21,6 +29,13 @@
// Uncomment to dump debug messages
//#define ZT_PORTMAPPER_TRACE 1
+#ifdef __ANDROID__
+#include <android/log.h>
+#define PM_TRACE(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "PortMapper", __VA_ARGS__))
+#else
+#define PM_TRACE(...) fprintf(stderr, __VA_ARGS__)
+#endif
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -45,15 +60,24 @@
#include <miniupnpc/miniupnpc.h>
#include <miniupnpc/upnpcommands.h>
#else
+#ifdef __ANDROID__
+#include "miniupnpc.h"
+#include "upnpcommands.h"
+#else
#include "../ext/miniupnpc/miniupnpc.h"
#include "../ext/miniupnpc/upnpcommands.h"
#endif
+#endif
#ifdef ZT_USE_SYSTEM_NATPMP
#include <natpmp.h>
#else
+#ifdef __ANDROID__
+#include "natpmp.h"
+#else
#include "../ext/libnatpmp/natpmp.h"
#endif
+#endif
namespace ZeroTier {
@@ -75,7 +99,7 @@ public:
int mode = 0; // 0 == NAT-PMP, 1 == UPnP
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: started for UDP port %d"ZT_EOL_S,localPort);
+ fprintf(stderr,"PortMapper: started for UDP port %d" ZT_EOL_S,localPort);
#endif
while (run) {
@@ -99,15 +123,16 @@ public:
if (initnatpmp(&natpmp,0,0) != 0) {
mode = 1;
+ closenatpmp(&natpmp);
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: NAT-PMP: init failed, switching to UPnP mode"ZT_EOL_S);
+ PM_TRACE("PortMapper: NAT-PMP: init failed, switching to UPnP mode" ZT_EOL_S);
#endif
break;
}
InetAddress publicAddress;
sendpublicaddressrequest(&natpmp);
- uint64_t myTimeout = OSUtils::now() + 5000;
+ int64_t myTimeout = OSUtils::now() + 5000;
do {
fd_set fds;
struct timeval timeout;
@@ -123,7 +148,7 @@ public:
publicAddress = InetAddress((uint32_t)response.pnu.publicaddress.addr.s_addr,0);
} else {
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: NAT-PMP: request for external address failed, aborting..."ZT_EOL_S);
+ PM_TRACE("PortMapper: NAT-PMP: request for external address failed, aborting..." ZT_EOL_S);
#endif
closenatpmp(&natpmp);
break;
@@ -145,7 +170,8 @@ public:
if (r == 0) {
publicAddress.setPort(response.pnu.newportmapping.mappedpublicport);
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: NAT-PMP: mapped %u to %s"ZT_EOL_S,(unsigned int)localPort,publicAddress.toString().c_str());
+ char paddr[128];
+ PM_TRACE("PortMapper: NAT-PMP: mapped %u to %s" ZT_EOL_S,(unsigned int)localPort,publicAddress.toString(paddr));
#endif
Mutex::Lock sl(surface_l);
surface.clear();
@@ -162,7 +188,7 @@ public:
if (!natPmpSuccess) {
mode = 1;
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: NAT-PMP: request failed, switching to UPnP mode"ZT_EOL_S);
+ PM_TRACE("PortMapper: NAT-PMP: request failed, switching to UPnP mode" ZT_EOL_S);
#endif
}
}
@@ -187,7 +213,7 @@ public:
{
UPNPDev *dev = devlist;
while (dev) {
- fprintf(stderr,"PortMapper: found UPnP device at URL '%s': %s"ZT_EOL_S,dev->descURL,dev->st);
+ PM_TRACE("PortMapper: found UPnP device at URL '%s': %s" ZT_EOL_S,dev->descURL,dev->st);
dev = dev->pNext;
}
}
@@ -197,22 +223,22 @@ public:
memset(externalip,0,sizeof(externalip));
memset(&urls,0,sizeof(urls));
memset(&data,0,sizeof(data));
- Utils::snprintf(inport,sizeof(inport),"%d",localPort);
+ OSUtils::ztsnprintf(inport,sizeof(inport),"%d",localPort);
if ((UPNP_GetValidIGD(devlist,&urls,&data,lanaddr,sizeof(lanaddr)))&&(lanaddr[0])) {
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: UPnP: my LAN IP address: %s"ZT_EOL_S,lanaddr);
+ PM_TRACE("PortMapper: UPnP: my LAN IP address: %s" ZT_EOL_S,lanaddr);
#endif
if ((UPNP_GetExternalIPAddress(urls.controlURL,data.first.servicetype,externalip) == UPNPCOMMAND_SUCCESS)&&(externalip[0])) {
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: UPnP: my external IP address: %s"ZT_EOL_S,externalip);
+ PM_TRACE("PortMapper: UPnP: my external IP address: %s" ZT_EOL_S,externalip);
#endif
for(int tries=0;tries<60;++tries) {
int tryPort = (int)localPort + tries;
if (tryPort >= 65535)
tryPort = (tryPort - 65535) + 1025;
- Utils::snprintf(outport,sizeof(outport),"%u",tryPort);
+ OSUtils::ztsnprintf(outport,sizeof(outport),"%u",tryPort);
// First check and see if this port is already mapped to the
// same unique name. If so, keep this mapping and don't try
@@ -231,7 +257,7 @@ public:
memset(haveLeaseDuration,0,sizeof(haveLeaseDuration));
if ((UPNP_GetSpecificPortMappingEntry(urls.controlURL,data.first.servicetype,outport,"UDP",(const char *)0,haveIntClient,haveIntPort,haveDesc,haveEnabled,haveLeaseDuration) == UPNPCOMMAND_SUCCESS)&&(uniqueName == haveDesc)) {
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: UPnP: reusing previously reserved external port: %s"ZT_EOL_S,outport);
+ PM_TRACE("PortMapper: UPnP: reusing previously reserved external port: %s" ZT_EOL_S,outport);
#endif
Mutex::Lock sl(surface_l);
surface.clear();
@@ -246,7 +272,7 @@ public:
int mapResult = 0;
if ((mapResult = UPNP_AddPortMapping(urls.controlURL,data.first.servicetype,outport,inport,lanaddr,uniqueName.c_str(),"UDP",(const char *)0,"0")) == UPNPCOMMAND_SUCCESS) {
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: UPnP: reserved external port: %s"ZT_EOL_S,outport);
+ PM_TRACE("PortMapper: UPnP: reserved external port: %s" ZT_EOL_S,outport);
#endif
Mutex::Lock sl(surface_l);
surface.clear();
@@ -256,7 +282,7 @@ public:
break;
} else {
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: UPnP: UPNP_AddPortMapping(%s) failed: %d"ZT_EOL_S,outport,mapResult);
+ PM_TRACE("PortMapper: UPnP: UPNP_AddPortMapping(%s) failed: %d" ZT_EOL_S,outport,mapResult);
#endif
Thread::sleep(1000);
}
@@ -265,13 +291,13 @@ public:
} else {
mode = 0;
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: UPnP: UPNP_GetExternalIPAddress failed, returning to NAT-PMP mode"ZT_EOL_S);
+ PM_TRACE("PortMapper: UPnP: UPNP_GetExternalIPAddress failed, returning to NAT-PMP mode" ZT_EOL_S);
#endif
}
} else {
mode = 0;
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: UPnP: UPNP_GetValidIGD failed, returning to NAT-PMP mode"ZT_EOL_S);
+ PM_TRACE("PortMapper: UPnP: UPNP_GetValidIGD failed, returning to NAT-PMP mode" ZT_EOL_S);
#endif
}
@@ -280,14 +306,14 @@ public:
} else {
mode = 0;
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: upnpDiscover failed, returning to NAT-PMP mode: %d"ZT_EOL_S,upnpError);
+ PM_TRACE("PortMapper: upnpDiscover failed, returning to NAT-PMP mode: %d" ZT_EOL_S,upnpError);
#endif
}
}
// ---------------------------------------------------------------------
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"UPNPClient: rescanning in %d ms"ZT_EOL_S,ZT_PORTMAPPER_REFRESH_DELAY);
+ PM_TRACE("UPNPClient: rescanning in %d ms" ZT_EOL_S,ZT_PORTMAPPER_REFRESH_DELAY);
#endif
Thread::sleep(ZT_PORTMAPPER_REFRESH_DELAY);
}
diff --git a/osdep/PortMapper.hpp b/osdep/PortMapper.hpp
index 0b8d15fc..fa3cdc31 100644
--- a/osdep/PortMapper.hpp
+++ b/osdep/PortMapper.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifdef ZT_USE_MINIUPNPC
diff --git a/osdep/TestEthernetTap.hpp b/osdep/TestEthernetTap.hpp
new file mode 100644
index 00000000..6ccf92f3
--- /dev/null
+++ b/osdep/TestEthernetTap.hpp
@@ -0,0 +1,161 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_TESTETHERNETTAP_HPP
+#define ZT_TESTETHERNETTAP_HPP
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <string>
+#include <vector>
+#include <stdexcept>
+#include <set>
+
+#include "../node/Constants.hpp"
+#include "../node/InetAddress.hpp"
+#include "../node/MulticastGroup.hpp"
+#include "../node/Mutex.hpp"
+#include "../node/Utils.hpp"
+#include "../osdep/OSUtils.hpp"
+
+namespace ZeroTier {
+
+/**
+ * Dummy test Ethernet tap that does not actually open a device on the system
+ */
+class TestEthernetTap
+{
+public:
+ TestEthernetTap(
+ const char *homePath,
+ const MAC &mac,
+ unsigned int mtu,
+ unsigned int metric,
+ uint64_t nwid,
+ const char *friendlyName,
+ void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
+ void *arg) :
+ _nwid(nwid),
+ _dev("zt_test_"),
+ _enabled(true)
+ {
+ char tmp[32];
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",(unsigned long long)_nwid);
+ _dev.append(tmp);
+#ifdef ZT_TEST_TAP_REPORT_TO
+ _reportTo.fromString(ZT_TEST_TAP_REPORT_TO);
+ if (_reportTo.ss_family == AF_INET)
+ _reportsock = socket(AF_INET,SOCK_DGRAM,0);
+ else if (_reportTo.ss_family == AF_INET6)
+ _reportsock = socket(AF_INET6,SOCK_DGRAM,0);
+ else _reportsock = -1;
+#endif
+ }
+
+ ~TestEthernetTap()
+ {
+#ifdef ZT_TEST_TAP_REPORT_TO
+ if (_reportsock >= 0)
+ close(_reportsock);
+#endif
+ }
+
+ inline void setEnabled(bool en) { _enabled = en; }
+ inline bool enabled() const { return _enabled; }
+
+ inline bool addIp(const InetAddress &ip)
+ {
+ Mutex::Lock _l(_lock);
+ _ips.insert(ip);
+ return true;
+ }
+
+ inline bool removeIp(const InetAddress &ip)
+ {
+ Mutex::Lock _l(_lock);
+ _ips.erase(ip);
+ return true;
+ }
+
+ inline std::vector<InetAddress> ips() const
+ {
+ Mutex::Lock _l(_lock);
+ return std::vector<InetAddress>(_ips.begin(),_ips.end());
+ }
+
+ inline void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
+ {
+#ifdef ZT_TEST_TAP_REPORT_TO
+ char tmp[10000];
+ if ((_reportsock >= 0)&&(len < (sizeof(tmp) - 22))) {
+ const uint64_t nwid2 = Utils::hton(_nwid);
+ memcpy(tmp,&nwid2,8);
+ from.copyTo(tmp + 8,6);
+ to.copyTo(tmp + 14,6);
+ const uint16_t etherType2 = Utils::hton((uint16_t)etherType);
+ memcpy(tmp + 20,&etherType2,2);
+ memcpy(tmp + 22,data,len);
+ sendto(_reportsock,tmp,len + 22,0,reinterpret_cast<const struct sockaddr *>(&_reportTo),(_reportTo.ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
+ }
+#endif
+ }
+
+ inline std::string deviceName() const
+ {
+ return _dev;
+ }
+
+ inline void setFriendlyName(const char *friendlyName)
+ {
+ }
+
+ inline void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed)
+ {
+ }
+
+ inline void setMtu(unsigned int mtu)
+ {
+ }
+
+private:
+ uint64_t _nwid;
+ std::string _dev;
+ std::set<InetAddress> _ips;
+ InetAddress _reportTo;
+#ifdef ZT_TEST_TAP_REPORT_TO
+ int _reportsock;
+#endif
+ bool _enabled;
+ Mutex _lock;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/osdep/Thread.hpp b/osdep/Thread.hpp
index 227c2cfe..35ea5035 100644
--- a/osdep/Thread.hpp
+++ b/osdep/Thread.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_THREAD_HPP
@@ -46,7 +54,6 @@ class Thread
{
public:
Thread()
- throw()
{
_th = NULL;
_tid = 0;
@@ -54,7 +61,6 @@ public:
template<typename C>
static inline Thread start(C *instance)
- throw(std::runtime_error)
{
Thread t;
t._th = CreateThread(NULL,0,&___zt_threadMain<C>,(LPVOID)instance,0,&t._tid);
@@ -84,11 +90,13 @@ public:
// Not available on *nix platforms
static inline void cancelIO(const Thread &t)
{
+#if !defined(__MINGW32__) && !defined(__MINGW64__) // CancelSynchronousIo not available in MSYS2
if (t._th != NULL)
CancelSynchronousIo(t._th);
+#endif
}
- inline operator bool() const throw() { return (_th != NULL); }
+ inline operator bool() const { return (_th != NULL); }
private:
HANDLE _th;
@@ -123,33 +131,18 @@ class Thread
{
public:
Thread()
- throw()
{
- memset(&_tid,0,sizeof(_tid));
- pthread_attr_init(&_tattr);
- // This corrects for systems with abnormally small defaults (musl) and also
- // shrinks the stack on systems with large defaults to save a bit of memory.
- pthread_attr_setstacksize(&_tattr,ZT_THREAD_MIN_STACK_SIZE);
- _started = false;
- }
-
- ~Thread()
- {
- pthread_attr_destroy(&_tattr);
+ memset(this,0,sizeof(Thread));
}
Thread(const Thread &t)
- throw()
{
- memcpy(&_tid,&(t._tid),sizeof(_tid));
- _started = t._started;
+ memcpy(this,&t,sizeof(Thread));
}
inline Thread &operator=(const Thread &t)
- throw()
{
- memcpy(&_tid,&(t._tid),sizeof(_tid));
- _started = t._started;
+ memcpy(this,&t,sizeof(Thread));
return *this;
}
@@ -163,12 +156,20 @@ public:
*/
template<typename C>
static inline Thread start(C *instance)
- throw(std::runtime_error)
{
Thread t;
- t._started = true;
- if (pthread_create(&t._tid,&t._tattr,&___zt_threadMain<C>,instance))
+ pthread_attr_t tattr;
+ pthread_attr_init(&tattr);
+ // This corrects for systems with abnormally small defaults (musl) and also
+ // shrinks the stack on systems with large defaults to save a bit of memory.
+ pthread_attr_setstacksize(&tattr,ZT_THREAD_MIN_STACK_SIZE);
+ if (pthread_create(&t._tid,&tattr,&___zt_threadMain<C>,instance)) {
+ pthread_attr_destroy(&tattr);
throw std::runtime_error("pthread_create() failed, unable to create thread");
+ } else {
+ t._started = true;
+ pthread_attr_destroy(&tattr);
+ }
return t;
}
@@ -190,11 +191,10 @@ public:
*/
static inline void sleep(unsigned long ms) { usleep(ms * 1000); }
- inline operator bool() const throw() { return (_started); }
+ inline operator bool() const { return (_started); }
private:
pthread_t _tid;
- pthread_attr_t _tattr;
volatile bool _started;
};
diff --git a/osdep/WindowsEthernetTap.cpp b/osdep/WindowsEthernetTap.cpp
index 79b9d35e..aa96d33a 100644
--- a/osdep/WindowsEthernetTap.cpp
+++ b/osdep/WindowsEthernetTap.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -462,7 +470,9 @@ WindowsEthernetTap::WindowsEthernetTap(
_arg(arg),
_mac(mac),
_nwid(nwid),
+ _mtu(mtu),
_tap(INVALID_HANDLE_VALUE),
+ _friendlyName(friendlyName),
_injectSemaphore(INVALID_HANDLE_VALUE),
_pathToHelpers(hp),
_run(true),
@@ -473,13 +483,9 @@ WindowsEthernetTap::WindowsEthernetTap(
char subkeyClass[1024];
char data[1024];
char tag[24];
- std::string mySubkeyName;
-
- if (mtu > 2800)
- throw std::runtime_error("MTU too large.");
// We "tag" registry entries with the network ID to identify persistent devices
- Utils::snprintf(tag,sizeof(tag),"%.16llx",(unsigned long long)nwid);
+ OSUtils::ztsnprintf(tag,sizeof(tag),"%.16llx",(unsigned long long)nwid);
Mutex::Lock _l(_systemTapInitLock);
@@ -522,7 +528,7 @@ WindowsEthernetTap::WindowsEthernetTap(
_netCfgInstanceId = instanceId;
_deviceInstanceId = instanceIdPath;
- mySubkeyName = subkeyName;
+ _mySubkeyName = subkeyName;
break; // found it!
}
}
@@ -565,7 +571,7 @@ WindowsEthernetTap::WindowsEthernetTap(
if (RegGetValueA(nwAdapters,subkeyName,"DeviceInstanceID",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS)
_deviceInstanceId.assign(data,dataLen);
- mySubkeyName = subkeyName;
+ _mySubkeyName = subkeyName;
// Disable DHCP by default on new devices
HKEY tcpIpInterfaces;
@@ -596,25 +602,25 @@ WindowsEthernetTap::WindowsEthernetTap(
if (_netCfgInstanceId.length() > 0) {
char tmps[64];
- unsigned int tmpsl = Utils::snprintf(tmps,sizeof(tmps),"%.2X-%.2X-%.2X-%.2X-%.2X-%.2X",(unsigned int)mac[0],(unsigned int)mac[1],(unsigned int)mac[2],(unsigned int)mac[3],(unsigned int)mac[4],(unsigned int)mac[5]) + 1;
- RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"NetworkAddress",REG_SZ,tmps,tmpsl);
- RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"MAC",REG_SZ,tmps,tmpsl);
- tmpsl = Utils::snprintf(tmps, sizeof(tmps), "%d", mtu);
- RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"MTU",REG_SZ,tmps,tmpsl);
+ unsigned int tmpsl = OSUtils::ztsnprintf(tmps,sizeof(tmps),"%.2X-%.2X-%.2X-%.2X-%.2X-%.2X",(unsigned int)mac[0],(unsigned int)mac[1],(unsigned int)mac[2],(unsigned int)mac[3],(unsigned int)mac[4],(unsigned int)mac[5]) + 1;
+ RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"NetworkAddress",REG_SZ,tmps,tmpsl);
+ RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"MAC",REG_SZ,tmps,tmpsl);
+ tmpsl = OSUtils::ztsnprintf(tmps, sizeof(tmps), "%d", mtu);
+ RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"MTU",REG_SZ,tmps,tmpsl);
DWORD tmp = 0;
- RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"*NdisDeviceType",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp));
+ RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"*NdisDeviceType",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp));
tmp = IF_TYPE_ETHERNET_CSMACD;
- RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"*IfType",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp));
+ RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"*IfType",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp));
if (creatingNewDevice) {
// Vista/2008 does not set this
if (newDeviceInstanceId.length() > 0)
- RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"DeviceInstanceID",REG_SZ,newDeviceInstanceId.c_str(),(DWORD)newDeviceInstanceId.length());
+ RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"DeviceInstanceID",REG_SZ,newDeviceInstanceId.c_str(),(DWORD)newDeviceInstanceId.length());
// Set EnableDHCP to 0 by default on new devices
tmp = 0;
- RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"EnableDHCP",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp));
+ RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"EnableDHCP",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp));
}
RegCloseKey(nwAdapters);
} else {
@@ -717,20 +723,21 @@ bool WindowsEthernetTap::removeIp(const InetAddress &ip)
DeleteUnicastIpAddressEntry(&(ipt->Table[i]));
FreeMibTable(ipt);
- if (ip.isV4()) {
- std::vector<std::string> regIps(_getRegistryIPv4Value("IPAddress"));
- std::vector<std::string> regSubnetMasks(_getRegistryIPv4Value("SubnetMask"));
- std::string ipstr(ip.toIpString());
- for (std::vector<std::string>::iterator rip(regIps.begin()), rm(regSubnetMasks.begin()); ((rip != regIps.end()) && (rm != regSubnetMasks.end())); ++rip, ++rm) {
- if (*rip == ipstr) {
- regIps.erase(rip);
- regSubnetMasks.erase(rm);
- _setRegistryIPv4Value("IPAddress", regIps);
- _setRegistryIPv4Value("SubnetMask", regSubnetMasks);
- break;
- }
- }
- }
+ if (ip.isV4()) {
+ std::vector<std::string> regIps(_getRegistryIPv4Value("IPAddress"));
+ std::vector<std::string> regSubnetMasks(_getRegistryIPv4Value("SubnetMask"));
+ char ipbuf[64];
+ std::string ipstr(ip.toIpString(ipbuf));
+ for (std::vector<std::string>::iterator rip(regIps.begin()), rm(regSubnetMasks.begin()); ((rip != regIps.end()) && (rm != regSubnetMasks.end())); ++rip, ++rm) {
+ if (*rip == ipstr) {
+ regIps.erase(rip);
+ regSubnetMasks.erase(rm);
+ _setRegistryIPv4Value("IPAddress", regIps);
+ _setRegistryIPv4Value("SubnetMask", regSubnetMasks);
+ break;
+ }
+ }
+ }
return true;
}
@@ -745,7 +752,7 @@ bool WindowsEthernetTap::removeIp(const InetAddress &ip)
std::vector<InetAddress> WindowsEthernetTap::ips() const
{
- static const InetAddress linkLocalLoopback("fe80::1",64); // what is this and why does Windows assign it?
+ static const InetAddress linkLocalLoopback("fe80::1/64"); // what is this and why does Windows assign it?
std::vector<InetAddress> addrs;
if (!_initialized)
@@ -784,12 +791,13 @@ std::vector<InetAddress> WindowsEthernetTap::ips() const
void WindowsEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
{
- if ((!_initialized)||(!_enabled)||(_tap == INVALID_HANDLE_VALUE)||(len > (ZT_IF_MTU)))
+ if ((!_initialized)||(!_enabled)||(_tap == INVALID_HANDLE_VALUE)||(len > _mtu))
return;
Mutex::Lock _l(_injectPending_m);
- _injectPending.push( std::pair<Array<char,ZT_IF_MTU + 32>,unsigned int>(Array<char,ZT_IF_MTU + 32>(),len + 14) );
- char *d = _injectPending.back().first.data;
+ _injectPending.emplace();
+ _injectPending.back().len = len + 14;
+ char *const d = _injectPending.back().data;
to.copyTo(d,6);
from.copyTo(d + 6,6);
d[12] = (char)((etherType >> 8) & 0xff);
@@ -833,7 +841,7 @@ void WindowsEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,
// pretty much anything work... IPv4, IPv6, IPX, oldskool Netbios, who knows...
unsigned char mcastbuf[TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS_OUTPUT_BUF_SIZE];
DWORD bytesReturned = 0;
- if (DeviceIoControl(t,TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS,(LPVOID)0,0,(LPVOID)mcastbuf,sizeof(mcastbuf),&bytesReturned,NULL)) {
+ if (DeviceIoControl(t,TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS,(LPVOID)mcastbuf,sizeof(mcastbuf),(LPVOID)mcastbuf,sizeof(mcastbuf),&bytesReturned,NULL)) {
if ((bytesReturned > 0)&&(bytesReturned <= TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS_OUTPUT_BUF_SIZE)) { // sanity check
MAC mac;
DWORD i = 0;
@@ -867,6 +875,20 @@ void WindowsEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,
_multicastGroups.swap(newGroups);
}
+void WindowsEthernetTap::setMtu(unsigned int mtu)
+{
+ if (mtu != _mtu) {
+ _mtu = mtu;
+ HKEY nwAdapters;
+ if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}", 0, KEY_READ | KEY_WRITE, &nwAdapters) == ERROR_SUCCESS) {
+ char tmps[64];
+ unsigned int tmpsl = OSUtils::ztsnprintf(tmps, sizeof(tmps), "%d", mtu);
+ RegSetKeyValueA(nwAdapters, _mySubkeyName.c_str(), "MTU", REG_SZ, tmps, tmpsl);
+ RegCloseKey(nwAdapters);
+ }
+ }
+}
+
NET_IFINDEX WindowsEthernetTap::interfaceIndex() const
{
NET_IFINDEX idx = -1;
@@ -878,12 +900,12 @@ NET_IFINDEX WindowsEthernetTap::interfaceIndex() const
void WindowsEthernetTap::threadMain()
throw()
{
- char tapReadBuf[ZT_IF_MTU + 32];
+ char tapReadBuf[ZT_MAX_MTU + 32];
char tapPath[128];
HANDLE wait4[3];
OVERLAPPED tapOvlRead,tapOvlWrite;
- Utils::snprintf(tapPath,sizeof(tapPath),"\\\\.\\Global\\%s.tap",_netCfgInstanceId.c_str());
+ OSUtils::ztsnprintf(tapPath,sizeof(tapPath),"\\\\.\\Global\\%s.tap",_netCfgInstanceId.c_str());
try {
while (_run) {
@@ -1007,13 +1029,18 @@ void WindowsEthernetTap::threadMain()
ReadFile(_tap,tapReadBuf,sizeof(tapReadBuf),NULL,&tapOvlRead);
bool writeInProgress = false;
ULONGLONG timeOfLastBorkCheck = GetTickCount64();
+ _initialized = true;
+ unsigned int oldmtu = _mtu;
-
- _initialized = true;
+ setFriendlyName(_friendlyName.c_str());
while (_run) {
DWORD waitResult = WaitForMultipleObjectsEx(writeInProgress ? 3 : 2,wait4,FALSE,2500,TRUE);
- if (!_run) break; // will also break outer while(_run)
+ if (!_run) break; // will also break outer while(_run) since _run is false
+
+ // Check for changes in MTU and break to restart tap device to reconfigure in this case
+ if (_mtu != oldmtu)
+ break;
// Check for issues with adapter and close/reopen if any are detected. This
// check fixes a while boatload of Windows adapter 'coma' issues after
@@ -1062,7 +1089,7 @@ void WindowsEthernetTap::threadMain()
} catch ( ... ) {} // handlers should not throw
}
}
- ReadFile(_tap,tapReadBuf,ZT_IF_MTU + 32,NULL,&tapOvlRead);
+ ReadFile(_tap,tapReadBuf,ZT_MAX_MTU + 32,NULL,&tapOvlRead);
}
if (writeInProgress) {
@@ -1074,7 +1101,7 @@ void WindowsEthernetTap::threadMain()
} else _injectPending_m.lock();
if (!_injectPending.empty()) {
- WriteFile(_tap,_injectPending.front().first.data,_injectPending.front().second,NULL,&tapOvlWrite);
+ WriteFile(_tap,_injectPending.front().data,_injectPending.front().len,NULL,&tapOvlWrite);
writeInProgress = true;
}
@@ -1204,18 +1231,18 @@ void WindowsEthernetTap::_syncIps()
CreateUnicastIpAddressEntry(&ipr);
}
- if (aip->isV4())
- {
- std::string ipStr(aip->toIpString());
- std::vector<std::string> regIps(_getRegistryIPv4Value("IPAddress"));
- if (std::find(regIps.begin(), regIps.end(), ipStr) == regIps.end()) {
- std::vector<std::string> regSubnetMasks(_getRegistryIPv4Value("SubnetMask"));
- regIps.push_back(ipStr);
- regSubnetMasks.push_back(aip->netmask().toIpString());
- _setRegistryIPv4Value("IPAddress", regIps);
- _setRegistryIPv4Value("SubnetMask", regSubnetMasks);
- }
- }
+ if (aip->isV4()) {
+ char ipbuf[64];
+ std::string ipStr(aip->toIpString(ipbuf));
+ std::vector<std::string> regIps(_getRegistryIPv4Value("IPAddress"));
+ if (std::find(regIps.begin(), regIps.end(), ipStr) == regIps.end()) {
+ std::vector<std::string> regSubnetMasks(_getRegistryIPv4Value("SubnetMask"));
+ regIps.push_back(ipStr);
+ regSubnetMasks.push_back(aip->netmask().toIpString(ipbuf));
+ _setRegistryIPv4Value("IPAddress", regIps);
+ _setRegistryIPv4Value("SubnetMask", regSubnetMasks);
+ }
+ }
}
}
diff --git a/osdep/WindowsEthernetTap.hpp b/osdep/WindowsEthernetTap.hpp
index f2cf73f3..1e36bdd8 100644
--- a/osdep/WindowsEthernetTap.hpp
+++ b/osdep/WindowsEthernetTap.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_WINDOWSETHERNETTAP_HPP
@@ -30,7 +38,6 @@
#include "../node/Constants.hpp"
#include "../node/Mutex.hpp"
-#include "../node/Array.hpp"
#include "../node/MulticastGroup.hpp"
#include "../node/InetAddress.hpp"
#include "../osdep/Thread.hpp"
@@ -101,6 +108,7 @@ public:
std::string deviceName() const;
void setFriendlyName(const char *friendlyName);
void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
+ void setMtu(unsigned int mtu);
inline const NET_LUID &luid() const { return _deviceLuid; }
inline const GUID &guid() const { return _deviceGuid; }
@@ -122,6 +130,7 @@ private:
void *_arg;
MAC _mac;
uint64_t _nwid;
+ volatile unsigned int _mtu;
Thread _thread;
volatile HANDLE _tap;
@@ -131,13 +140,21 @@ private:
NET_LUID _deviceLuid;
std::string _netCfgInstanceId;
std::string _deviceInstanceId;
+ std::string _mySubkeyName;
+
+ std::string _friendlyName;
std::vector<InetAddress> _assignedIps; // IPs assigned with addIp
Mutex _assignedIps_m;
std::vector<MulticastGroup> _multicastGroups;
- std::queue< std::pair< Array<char,ZT_IF_MTU + 32>,unsigned int > > _injectPending;
+ struct _InjectPending
+ {
+ unsigned int len;
+ char data[ZT_MAX_MTU + 32];
+ };
+ std::queue<_InjectPending> _injectPending;
Mutex _injectPending_m;
std::string _pathToHelpers;
diff --git a/root-watcher/README.md b/root-watcher/README.md
deleted file mode 100644
index ded6a63f..00000000
--- a/root-watcher/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-Root Server Watcher
-======
-
-This is a small daemon written in NodeJS that watches a set of root servers and records peer status information into a Postgres database.
-
-To use type `npm install` to install modules. Then edit `config.json.example` and rename to `config.json`. For each of your roots you will need to configure a way for this script to reach it. You will also need to use `schema.sql` to initialize a Postgres database to contain your logs and set it up in `config.json` as well.
-
-This doesn't (yet) include any software for reading the log database and doing anything useful with the information inside, though given that it's a simple SQL database it should not be hard to compose queries to show interesting statistics.
diff --git a/root-watcher/config.json.example b/root-watcher/config.json.example
deleted file mode 100644
index 0ad1bbe1..00000000
--- a/root-watcher/config.json.example
+++ /dev/null
@@ -1,30 +0,0 @@
-{
- "interval": 30000,
- "dbSaveInterval": 60000,
- "peerTimeout": 600000,
- "db": {
- "database": "ztr",
- "user": "postgres",
- "password": "s00p3rs3kr3t",
- "host": "127.0.0.1",
- "port": 5432,
- "max": 16,
- "idleTimeoutMillis": 30000
- },
- "roots": {
- "my-root-01": {
- "id": 1,
- "ip": "10.0.0.1",
- "port": 9993,
- "authToken": "foobarbaz",
- "peers": "/peer"
- },
- "my-root-02": {
- "id": 2,
- "ip": "10.0.0.2",
- "port": 9993,
- "authToken": "lalafoo",
- "peers": "/peer"
- }
- }
-}
diff --git a/root-watcher/package.json b/root-watcher/package.json
deleted file mode 100644
index d6e86d78..00000000
--- a/root-watcher/package.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "name": "zerotier-root-watcher",
- "version": "1.0.0",
- "description": "Simple background service to watch a cluster of roots and record peer info into a database",
- "main": "zerotier-root-watcher.js",
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
- },
- "author": "ZeroTier, Inc. <contact@zerotier.com>",
- "license": "GPL-3.0",
- "dependencies": {
- "async": "^2.3.0",
- "pg": "^6.1.5",
- "zlib": "^1.0.5"
- }
-}
diff --git a/root-watcher/schema.sql b/root-watcher/schema.sql
deleted file mode 100644
index bdb3a1cf..00000000
--- a/root-watcher/schema.sql
+++ /dev/null
@@ -1,21 +0,0 @@
-/* Schema for ZeroTier root watcher log database */
-
-/* If you cluster this DB using any PG clustering scheme that uses logs, you must remove UNLOGGED here! */
-CREATE UNLOGGED TABLE "Peer"
-(
- "ztAddress" BIGINT NOT NULL,
- "timestamp" BIGINT NOT NULL,
- "versionMajor" INTEGER NOT NULL,
- "versionMinor" INTEGER NOT NULL,
- "versionRev" INTEGER NOT NULL,
- "rootId" INTEGER NOT NULL,
- "phyPort" INTEGER NOT NULL,
- "phyLinkQuality" REAL NOT NULL,
- "phyLastReceive" BIGINT NOT NULL,
- "phyAddress" INET NOT NULL
-);
-
-CREATE INDEX "Peer_ztAddress" ON "Peer" ("ztAddress");
-CREATE INDEX "Peer_timestamp" ON "Peer" ("timestamp");
-CREATE INDEX "Peer_rootId" ON "Peer" ("rootId");
-CREATE INDEX "Peer_phyAddress" ON "Peer" ("phyAddress");
diff --git a/root-watcher/zerotier-root-watcher.js b/root-watcher/zerotier-root-watcher.js
deleted file mode 100644
index d4607fc2..00000000
--- a/root-watcher/zerotier-root-watcher.js
+++ /dev/null
@@ -1,235 +0,0 @@
-'use strict';
-
-const pg = require('pg');
-const zlib = require('zlib');
-const http = require('http');
-const fs = require('fs');
-const async = require('async');
-
-const config = JSON.parse(fs.readFileSync('./config.json'));
-const roots = config.roots||{};
-
-const db = new pg.Pool(config.db);
-
-process.on('uncaughtException',function(err) {
- console.error('ERROR: uncaught exception: '+err);
- if (err.stack)
- console.error(err.stack);
-});
-
-function httpRequest(host,port,authToken,method,path,args,callback)
-{
- var responseBody = [];
- var postData = (args) ? JSON.stringify(args) : null;
-
- var req = http.request({
- host: host,
- port: port,
- path: path,
- method: method,
- headers: {
- 'X-ZT1-Auth': (authToken||''),
- 'Content-Length': (postData) ? postData.length : 0
- }
- },function(res) {
- res.on('data',function(chunk) {
- if ((chunk)&&(chunk.length > 0))
- responseBody.push(chunk);
- });
- res.on('timeout',function() {
- try {
- if (typeof callback === 'function') {
- var cb = callback;
- callback = null;
- cb(new Error('connection timed out'),null);
- }
- req.abort();
- } catch (e) {}
- });
- res.on('error',function(e) {
- try {
- if (typeof callback === 'function') {
- var cb = callback;
- callback = null;
- cb(new Error('connection timed out'),null);
- }
- req.abort();
- } catch (e) {}
- });
- res.on('end',function() {
- if (typeof callback === 'function') {
- var cb = callback;
- callback = null;
- if (responseBody.length === 0) {
- return cb(null,{});
- } else {
- responseBody = Buffer.concat(responseBody);
-
- if (responseBody.length < 2) {
- return cb(null,{});
- }
-
- if ((responseBody.readUInt8(0,true) === 0x1f)&&(responseBody.readUInt8(1,true) === 0x8b)) {
- try {
- responseBody = zlib.gunzipSync(responseBody);
- } catch (e) {
- return cb(e,null);
- }
- }
-
- try {
- return cb(null,JSON.parse(responseBody));
- } catch (e) {
- return cb(e,null);
- }
- }
- }
- });
- }).on('error',function(e) {
- try {
- if (typeof callback === 'function') {
- var cb = callback;
- callback = null;
- cb(e,null);
- }
- req.abort();
- } catch (e) {}
- }).on('timeout',function() {
- try {
- if (typeof callback === 'function') {
- var cb = callback;
- callback = null;
- cb(new Error('connection timed out'),null);
- }
- req.abort();
- } catch (e) {}
- });
-
- req.setTimeout(30000);
- req.setNoDelay(true);
-
- if (postData !== null)
- req.end(postData);
- else req.end();
-};
-
-var peerStatus = {};
-
-function saveToDb()
-{
- db.connect(function(err,client,clientDone) {
- if (err) {
- console.log('WARNING: database error writing peers: '+err.toString());
- clientDone();
- return setTimeout(saveToDb,config.dbSaveInterval||60000);
- }
- client.query('BEGIN',function(err) {
- if (err) {
- console.log('WARNING: database error writing peers: '+err.toString());
- clientDone();
- return setTimeout(saveToDb,config.dbSaveInterval||60000);
- }
- let timeout = Date.now() - (config.peerTimeout||600000);
- let wtotal = 0;
- async.eachSeries(Object.keys(peerStatus),function(address,nextAddress) {
- let s = peerStatus[address];
- if (s[1] <= timeout) {
- delete peerStatus[address];
- return process.nextTick(nextAddress);
- } else {
- ++wtotal;
- client.query('INSERT INTO "Peer" ("ztAddress","timestamp","versionMajor","versionMinor","versionRev","rootId","phyPort","phyLinkQuality","phyLastReceive","phyAddress") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)',s,nextAddress);
- }
- },function(err) {
- if (err)
- console.log('WARNING database error writing peers: '+err.toString());
- console.log(Date.now().toString()+' '+wtotal);
- client.query('COMMIT',function(err,result) {
- clientDone();
- return setTimeout(saveToDb,config.dbSaveInterval||60000);
- });
- });
- });
- });
-};
-
-function doRootUpdate(name,id,ip,port,peersPath,authToken,interval)
-{
- httpRequest(ip,port,authToken,"GET",peersPath,null,function(err,res) {
- if (err) {
- console.log('WARNING: cannot reach '+name+peersPath+' (will try again in 1s): '+err.toString());
- return setTimeout(function() { doRootUpdate(name,id,ip,port,peersPath,authToken,interval); },1000);
- }
- if (!Array.isArray(res)) {
- console.log('WARNING: cannot reach '+name+peersPath+' (will try again in 1s): response is not an array of peers');
- return setTimeout(function() { doRootUpdate(name,id,ip,port,peersPath,authToken,interval); },1000);
- }
-
- //console.log(name+': '+res.length+' peer entries.');
- let now = Date.now();
- let count = 0;
- for(let pi=0;pi<res.length;++pi) {
- let peer = res[pi];
- let address = peer.address;
- let ztAddress = parseInt(address,16)||0;
- if (!ztAddress)
- continue;
-
- let paths = peer.paths;
- if ((Array.isArray(paths))&&(paths.length > 0)) {
- let bestPath = null;
- for(let i=0;i<paths.length;++i) {
- if (paths[i].active) {
- let lr = paths[i].lastReceive;
- if ((lr > 0)&&((!bestPath)||(bestPath.lastReceive < lr)))
- bestPath = paths[i];
- }
- }
-
- if (bestPath) {
- let a = bestPath.address;
- if (typeof a === 'string') {
- let a2 = a.split('/');
- if (a2.length === 2) {
- let vmaj = peer.versionMajor;
- if ((typeof vmaj === 'undefined')||(vmaj === null)) vmaj = -1;
- let vmin = peer.versionMinor;
- if ((typeof vmin === 'undefined')||(vmin === null)) vmin = -1;
- let vrev = peer.versionRev;
- if ((typeof vrev === 'undefined')||(vrev === null)) vrev = -1;
- let lr = parseInt(bestPath.lastReceive)||0;
-
- let s = peerStatus[address];
- if ((!s)||(s[8] < lr)) {
- peerStatus[address] = [
- ztAddress,
- now,
- vmaj,
- vmin,
- vrev,
- id,
- parseInt(a2[1])||0,
- parseFloat(bestPath.linkQuality)||1.0,
- lr,
- a2[0]
- ];
- }
- ++count;
- }
- }
- }
- }
- }
-
- console.log(name+': '+count+' peers with active direct paths.');
- return setTimeout(function() { doRootUpdate(name,id,ip,port,peersPath,authToken,interval); },interval);
- });
-};
-
-for(var r in roots) {
- var rr = roots[r];
- if (rr.peers)
- doRootUpdate(r,rr.id,rr.ip,rr.port,rr.peers,rr.authToken||null,config.interval||60000);
-}
-
-return setTimeout(saveToDb,config.dbSaveInterval||60000);
diff --git a/selftest.cpp b/selftest.cpp
index 91d304a6..4008cd2a 100644
--- a/selftest.cpp
+++ b/selftest.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -25,6 +33,7 @@
#include <iostream>
#include <string>
#include <vector>
+#include <thread>
#include "node/Constants.hpp"
#include "node/Hashtable.hpp"
@@ -48,12 +57,9 @@
#include "osdep/OSUtils.hpp"
#include "osdep/Phy.hpp"
-#include "osdep/Http.hpp"
#include "osdep/PortMapper.hpp"
#include "osdep/Thread.hpp"
-#include "controller/JSONDB.hpp"
-
#ifdef ZT_USE_X64_ASM_SALSA2012
#include "ext/x64-salsa2012-asm/salsa2012.h"
#endif
@@ -143,12 +149,13 @@ static const C25519TestVector C25519_TEST_VECTORS[ZT_NUM_C25519_TEST_VECTORS] =
static int testCrypto()
{
- unsigned char buf1[16384];
- unsigned char buf2[sizeof(buf1)],buf3[sizeof(buf1)];
+ static unsigned char buf1[16384];
+ static unsigned char buf2[sizeof(buf1)],buf3[sizeof(buf1)];
+ static char hexbuf[1024];
for(int i=0;i<3;++i) {
Utils::getSecureRandom(buf1,64);
- std::cout << "[crypto] getSecureRandom: " << Utils::hex(buf1,64) << std::endl;
+ std::cout << "[crypto] getSecureRandom: " << Utils::hex(buf1,64,hexbuf) << std::endl;
}
std::cout << "[crypto] Testing Salsa20... "; std::cout.flush();
@@ -205,7 +212,7 @@ static int testCrypto()
}
uint64_t end = OSUtils::now();
SHA512::hash(buf1,bb,1234567);
- std::cout << ((bytes / 1048576.0) / ((long double)(end - start) / 1024.0)) << " MiB/second (" << Utils::hex(buf1,16) << ')' << std::endl;
+ std::cout << ((bytes / 1048576.0) / ((long double)(end - start) / 1024.0)) << " MiB/second (" << Utils::hex(buf1,16,hexbuf) << ')' << std::endl;
::free((void *)bb);
}
@@ -257,7 +264,7 @@ static int testCrypto()
}
uint64_t end = OSUtils::now();
SHA512::hash(buf1,bb,1234567);
- std::cout << ((bytes / 1048576.0) / ((long double)(end - start) / 1024.0)) << " MiB/second (" << Utils::hex(buf1,16) << ')' << std::endl;
+ std::cout << ((bytes / 1048576.0) / ((long double)(end - start) / 1024.0)) << " MiB/second (" << Utils::hex(buf1,16,hexbuf) << ')' << std::endl;
::free((void *)bb);
}
@@ -314,10 +321,10 @@ static int testCrypto()
std::cout << "[crypto] Testing C25519 and Ed25519 against test vectors... "; std::cout.flush();
for(int k=0;k<ZT_NUM_C25519_TEST_VECTORS;++k) {
C25519::Pair p1,p2;
- memcpy(p1.pub.data,C25519_TEST_VECTORS[k].pub1,p1.pub.size());
- memcpy(p1.priv.data,C25519_TEST_VECTORS[k].priv1,p1.priv.size());
- memcpy(p2.pub.data,C25519_TEST_VECTORS[k].pub2,p2.pub.size());
- memcpy(p2.priv.data,C25519_TEST_VECTORS[k].priv2,p2.priv.size());
+ memcpy(p1.pub.data,C25519_TEST_VECTORS[k].pub1,ZT_C25519_PUBLIC_KEY_LEN);
+ memcpy(p1.priv.data,C25519_TEST_VECTORS[k].priv1,ZT_C25519_PRIVATE_KEY_LEN);
+ memcpy(p2.pub.data,C25519_TEST_VECTORS[k].pub2,ZT_C25519_PUBLIC_KEY_LEN);
+ memcpy(p2.priv.data,C25519_TEST_VECTORS[k].priv2,ZT_C25519_PRIVATE_KEY_LEN);
C25519::agree(p1,p2.pub,buf1,64);
C25519::agree(p2,p1.pub,buf2,64);
if (memcmp(buf1,buf2,64)) {
@@ -369,11 +376,11 @@ static int testCrypto()
C25519::Pair bp[8];
for(int k=0;k<8;++k)
bp[k] = C25519::generate();
- const uint64_t st = OSUtils::now();
+ uint64_t st = OSUtils::now();
for(unsigned int k=0;k<50;++k) {
C25519::agree(bp[~k & 7],bp[k & 7].pub,buf1,64);
}
- const uint64_t et = OSUtils::now();
+ uint64_t et = OSUtils::now();
std::cout << ((double)(et - st) / 50.0) << "ms per agreement." << std::endl;
std::cout << "[crypto] Testing Ed25519 ECC signatures... "; std::cout.flush();
@@ -403,7 +410,7 @@ static int testCrypto()
}
for(unsigned int k=0;k<64;++k) {
C25519::Signature sig2(sig);
- sig2.data[rand() % sig2.size()] ^= (unsigned char)(1 << (rand() & 7));
+ sig2.data[rand() % ZT_C25519_SIGNATURE_LEN] ^= (unsigned char)(1 << (rand() & 7));
if (C25519::verify(p1.pub,buf1,sizeof(buf1),sig2)) {
std::cout << "FAIL (5)" << std::endl;
return -1;
@@ -412,6 +419,15 @@ static int testCrypto()
}
std::cout << "PASS" << std::endl;
+ std::cout << "[crypto] Benchmarking Ed25519 ECC signatures... "; std::cout.flush();
+ st = OSUtils::now();
+ for(int k=0;k<1000;++k) {
+ C25519::Signature sig;
+ C25519::sign(didntSign.priv,didntSign.pub,buf1,sizeof(buf1),sig.data);
+ }
+ et = OSUtils::now();
+ std::cout << ((double)(et - st) / 50.0) << "ms per signature." << std::endl;
+
return 0;
}
@@ -419,6 +435,7 @@ static int testIdentity()
{
Identity id;
Buffer<512> buf;
+ char buf2[1024];
std::cout << "[identity] Validate known-good identity... "; std::cout.flush();
if (!id.fromString(KNOWN_GOOD_IDENTITY)) {
@@ -451,7 +468,7 @@ static int testIdentity()
uint64_t genstart = OSUtils::now();
id.generate();
uint64_t genend = OSUtils::now();
- std::cout << "(took " << (genend - genstart) << "ms): " << id.toString(true) << std::endl;
+ std::cout << "(took " << (genend - genstart) << "ms): " << id.toString(true,buf2) << std::endl;
std::cout << "[identity] Locally validate identity: ";
if (id.locallyValidate()) {
std::cout << "PASS" << std::endl;
@@ -491,7 +508,7 @@ static int testIdentity()
{
Identity id2;
- id2.fromString(id.toString(true).c_str());
+ id2.fromString(id.toString(true,buf2));
std::cout << "[identity] Serialize and deserialize (ASCII w/private): ";
if ((id == id2)&&(id2.locallyValidate())) {
std::cout << "PASS" << std::endl;
@@ -503,7 +520,7 @@ static int testIdentity()
{
Identity id2;
- id2.fromString(id.toString(false).c_str());
+ id2.fromString(id.toString(false,buf2));
std::cout << "[identity] Serialize and deserialize (ASCII no private): ";
if ((id == id2)&&(id2.locallyValidate())) {
std::cout << "PASS" << std::endl;
@@ -518,16 +535,18 @@ static int testIdentity()
static int testCertificate()
{
+ char buf[4096];
+
Identity authority;
std::cout << "[certificate] Generating identity to act as authority... "; std::cout.flush();
authority.generate();
- std::cout << authority.address().toString() << std::endl;
+ std::cout << authority.address().toString(buf) << std::endl;
Identity idA,idB;
std::cout << "[certificate] Generating identities A and B... "; std::cout.flush();
idA.generate();
idB.generate();
- std::cout << idA.address().toString() << ", " << idB.address().toString() << std::endl;
+ std::cout << idA.address().toString(buf) << ", " << idB.address().toString(buf) << std::endl;
std::cout << "[certificate] Generating certificates A and B...";
CertificateOfMembership cA(10000,100,1,idA.address());
@@ -611,7 +630,7 @@ static int testPacket()
return -1;
}
- a.armor(salsaKey,true,0);
+ a.armor(salsaKey,true);
if (!a.dearmor(salsaKey)) {
std::cout << "FAIL (encrypt-decrypt/verify)" << std::endl;
return -1;
@@ -621,32 +640,79 @@ static int testPacket()
return 0;
}
-static void _testExcept(int &depth)
+static int testOther()
{
- if (depth >= 16) {
- throw std::runtime_error("LOL!");
+ char buf[1024];
+ char buf2[4096];
+ char buf3[1024];
+
+ std::cout << "[other] Testing hex/unhex... "; std::cout.flush();
+ Utils::getSecureRandom(buf,(unsigned int)sizeof(buf));
+ Utils::hex(buf,(unsigned int)sizeof(buf),buf2);
+ Utils::unhex(buf2,buf3,(unsigned int)sizeof(buf3));
+ if (memcmp(buf,buf3,sizeof(buf)) == 0) {
+ std::cout << "PASS" << std::endl;
} else {
- ++depth;
- _testExcept(depth);
+ std::cout << "FAIL!" << std::endl;
+ buf2[78] = 0;
+ std::cout << buf2 << std::endl;
+ Utils::hex(buf3,(unsigned int)sizeof(buf3),buf2);
+ buf2[78] = 0;
+ std::cout << buf2 << std::endl;
+ return -1;
}
-}
-static int testOther()
-{
- std::cout << "[other] Testing C++ exceptions... "; std::cout.flush();
- int depth = 0;
- try {
- _testExcept(depth);
- } catch (std::runtime_error &e) {
- if (depth == 16) {
- std::cout << "OK" << std::endl;
- } else {
- std::cout << "ERROR (depth not 16)" << std::endl;
- return -1;
+ std::cout << "[other] Testing InetAddress encode/decode..."; std::cout.flush();
+ std::cout << " " << InetAddress("127.0.0.1/9993").toString(buf);
+ std::cout << " " << InetAddress("feed:dead:babe:dead:beef:f00d:1234:5678/12345").toString(buf);
+ std::cout << " " << InetAddress("0/9993").toString(buf);
+ std::cout << " " << InetAddress("").toString(buf);
+ std::cout << std::endl;
+
+#if 0
+ std::cout << "[other] Benchmarking memcpy... "; std::cout.flush();
+ {
+ unsigned char *bb = (unsigned char *)::malloc(1234567);
+ unsigned char *cc = (unsigned char *)::malloc(1234567);
+ for(unsigned int i=0;i<1234567;++i)
+ bb[i] = (unsigned char)i;
+ double bytes = 0.0;
+ uint64_t start = OSUtils::now();
+ for(unsigned int i=0;i<20000;++i) {
+ ++bb[i];
+ ++bb[i+1];
+ memcpy(cc,bb,1234567);
+ bytes += 1234567.0;
}
- } catch ( ... ) {
- std::cout << "ERROR (exception not std::runtime_error)" << std::endl;
- return -1;
+ if (cc[0] != bb[0])
+ abort();
+ uint64_t end = OSUtils::now();
+ std::cout << ((bytes / 1048576.0) / ((long double)(end - start) / 1024.0)) << " MiB/second" << std::endl;
+ ::free((void *)bb);
+ ::free((void *)cc);
+ }
+#endif
+
+ std::cout << "[other] Benchmarking ZT_FAST_MEMCPY... "; std::cout.flush();
+ {
+ unsigned char *bb = (unsigned char *)::malloc(1234567);
+ unsigned char *cc = (unsigned char *)::malloc(1234567);
+ for(unsigned int i=0;i<1234567;++i)
+ bb[i] = (unsigned char)i;
+ double bytes = 0.0;
+ uint64_t start = OSUtils::now();
+ for(unsigned int i=0;i<20000;++i) {
+ ++bb[0];
+ ++bb[1234566];
+ ZT_FAST_MEMCPY(cc,bb,1234567);
+ bytes += 1234567.0;
+ }
+ if (cc[0] != bb[0])
+ abort();
+ uint64_t end = OSUtils::now();
+ std::cout << ((bytes / 1048576.0) / ((long double)(end - start) / 1024.0)) << " MiB/second" << std::endl;
+ ::free((void *)bb);
+ ::free((void *)cc);
}
#if 0
@@ -823,7 +889,7 @@ static int testOther()
memset(key, 0, sizeof(key));
memset(value, 0, sizeof(value));
for(unsigned int q=0;q<32;++q) {
- Utils::snprintf(key[q],16,"%.8lx",(unsigned long)(rand() % 1000) + (q * 1000));
+ Utils::hex((uint32_t)((rand() % 1000) + (q * 1000)),key[q]);
int r = rand() % 128;
for(int x=0;x<r;++x)
value[q][x] = ("0123456789\0\t\r\n= ")[rand() % 16];
@@ -1018,51 +1084,6 @@ static int testPhy()
return 0;
}
-/*
-static int testHttp()
-{
- std::map<std::string,std::string> requestHeaders,responseHeaders;
- std::string responseBody;
-
- InetAddress downloadZerotierDotCom;
- std::vector<InetAddress> rr(OSUtils::resolve("download.zerotier.com"));
- if (rr.empty()) {
- std::cout << "[http] Resolve of download.zerotier.com failed, skipping." << std::endl;
- return 0;
- } else {
- for(std::vector<InetAddress>::iterator r(rr.begin());r!=rr.end();++r) {
- std::cout << "[http] download.zerotier.com: " << r->toString() << std::endl;
- if (r->isV4())
- downloadZerotierDotCom = *r;
- }
- }
- downloadZerotierDotCom.setPort(80);
-
- std::cout << "[http] GET http://download.zerotier.com/dev/1k @" << downloadZerotierDotCom.toString() << " ... "; std::cout.flush();
- requestHeaders["Host"] = "download.zerotier.com";
- unsigned int sc = Http::GET(1024 * 1024 * 16,60000,reinterpret_cast<const struct sockaddr *>(&downloadZerotierDotCom),"/dev/1k",requestHeaders,responseHeaders,responseBody);
- std::cout << sc << " " << responseBody.length() << " bytes ";
- if (sc == 0)
- std::cout << "ERROR: " << responseBody << std::endl;
- else std::cout << "DONE" << std::endl;
-
- std::cout << "[http] GET http://download.zerotier.com/dev/4m @" << downloadZerotierDotCom.toString() << " ... "; std::cout.flush();
- requestHeaders["Host"] = "download.zerotier.com";
- sc = Http::GET(1024 * 1024 * 16,60000,reinterpret_cast<const struct sockaddr *>(&downloadZerotierDotCom),"/dev/4m",requestHeaders,responseHeaders,responseBody);
- std::cout << sc << " " << responseBody.length() << " bytes ";
- if (sc == 0)
- std::cout << "ERROR: " << responseBody << std::endl;
- else std::cout << "DONE" << std::endl;
-
- downloadZerotierDotCom = InetAddress("1.0.0.1/1234");
- std::cout << "[http] GET @" << downloadZerotierDotCom.toString() << " ... "; std::cout.flush();
- sc = Http::GET(1024 * 1024 * 16,2500,reinterpret_cast<const struct sockaddr *>(&downloadZerotierDotCom),"/dev/4m",requestHeaders,responseHeaders,responseBody);
- std::cout << sc << " (should be 0, time out)" << std::endl;
-
- return 0;
-}
-*/
-
#ifdef __WINDOWS__
int __cdecl _tmain(int argc, _TCHAR* argv[])
#else
@@ -1114,6 +1135,8 @@ int main(int argc,char **argv)
*/
std::cout << "[info] sizeof(void *) == " << sizeof(void *) << std::endl;
+ std::cout << "[info] OSUtils::now() == " << OSUtils::now() << std::endl;
+ std::cout << "[info] hardware concurrency == " << std::thread::hardware_concurrency() << std::endl;
std::cout << "[info] sizeof(NetworkConfig) == " << sizeof(ZeroTier::NetworkConfig) << std::endl;
srand((unsigned int)time(0));
@@ -1125,7 +1148,6 @@ int main(int argc,char **argv)
r |= testIdentity();
r |= testCertificate();
r |= testPhy();
- //r |= testHttp();
//*/
if (r)
diff --git a/service/OneService.cpp b/service/OneService.cpp
index b151d25d..abbd6e75 100644
--- a/service/OneService.cpp
+++ b/service/OneService.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -23,7 +31,6 @@
#include <string>
#include <map>
-#include <set>
#include <vector>
#include <algorithm>
#include <list>
@@ -39,6 +46,9 @@
#include "../node/MAC.hpp"
#include "../node/Identity.hpp"
#include "../node/World.hpp"
+#include "../node/Salsa20.hpp"
+#include "../node/Poly1305.hpp"
+#include "../node/SHA512.hpp"
#include "../osdep/Phy.hpp"
#include "../osdep/Thread.hpp"
@@ -49,8 +59,6 @@
#include "../osdep/ManagedRoute.hpp"
#include "OneService.hpp"
-#include "ClusterGeoIpService.hpp"
-#include "ClusterDefinition.hpp"
#include "SoftwareUpdater.hpp"
#ifdef __WINDOWS__
@@ -77,26 +85,24 @@
using json = nlohmann::json;
-/**
- * Uncomment to enable UDP breakage switch
- *
- * If this is defined, the presence of a file called /tmp/ZT_BREAK_UDP
- * will cause direct UDP TX/RX to stop working. This can be used to
- * test TCP tunneling fallback and other robustness features. Deleting
- * this file will cause it to start working again.
- */
-//#define ZT_BREAK_UDP
-
#include "../controller/EmbeddedNetworkController.hpp"
-// Include the right tap device driver for this platform -- add new platforms here
-#ifdef ZT_SERVICE_NETCON
+#ifdef ZT_USE_TEST_TAP
+
+#include "../osdep/TestEthernetTap.hpp"
+namespace ZeroTier { typedef TestEthernetTap EthernetTap; }
-// In network containers builds, use the virtual netcon endpoint instead of a tun/tap port driver
-#include "../netcon/NetconEthernetTap.hpp"
-namespace ZeroTier { typedef NetconEthernetTap EthernetTap; }
+#else
-#else // not ZT_SERVICE_NETCON so pick a tap driver
+#ifdef ZT_SDK
+
+#include "../controller/EmbeddedNetworkController.hpp"
+#include "../node/Node.hpp"
+// Use the virtual netcon endpoint instead of a tun/tap port driver
+#include "../include/VirtualTap.h"
+namespace ZeroTier { typedef VirtualTap EthernetTap; }
+
+#else
#ifdef __APPLE__
#include "../osdep/OSXEthernetTap.hpp"
@@ -121,9 +127,11 @@ namespace ZeroTier { typedef BSDEthernetTap EthernetTap; }
#endif // ZT_SERVICE_NETCON
+#endif // ZT_USE_TEST_TAP
+
// Sanity limits for HTTP
#define ZT_MAX_HTTP_MESSAGE_SIZE (1024 * 1024 * 64)
-#define ZT_MAX_HTTP_CONNECTIONS 64
+#define ZT_MAX_HTTP_CONNECTIONS 65536
// Interface metric for ZeroTier taps -- this ensures that if we are on WiFi and also
// bridged via ZeroTier to the same LAN traffic will (if the OS is sane) prefer WiFi.
@@ -132,9 +140,6 @@ namespace ZeroTier { typedef BSDEthernetTap EthernetTap; }
// How often to check for new multicast subscriptions on a tap device
#define ZT_TAP_CHECK_MULTICAST_INTERVAL 5000
-// Path under ZT1 home for controller database if controller is enabled
-#define ZT_CONTROLLER_DB_PATH "controller.d"
-
// TCP fallback relay (run by ZeroTier, Inc. -- this will eventually go away)
#define ZT_TCP_FALLBACK_RELAY "204.80.128.1/443"
@@ -147,13 +152,19 @@ namespace ZeroTier { typedef BSDEthernetTap EthernetTap; }
// How often to check for local interface addresses
#define ZT_LOCAL_INTERFACE_CHECK_INTERVAL 60000
-// Clean files from iddb.d that are older than this (60 days)
-#define ZT_IDDB_CLEANUP_AGE 5184000000ULL
+// Maximum write buffer size for outgoing TCP connections (sanity limit)
+#define ZT_TCP_MAX_WRITEQ_SIZE 33554432
+
+// TCP activity timeout
+#define ZT_TCP_ACTIVITY_TIMEOUT 60000
namespace ZeroTier {
namespace {
+// Fake TLS hello for TCP tunnel outgoing connections (TUNNELED mode)
+static const char ZT_TCP_TUNNEL_HELLO[9] = { 0x17,0x03,0x03,0x00,0x04,(char)ZEROTIER_ONE_VERSION_MAJOR,(char)ZEROTIER_ONE_VERSION_MINOR,(char)((ZEROTIER_ONE_VERSION_REVISION >> 8) & 0xff),(char)(ZEROTIER_ONE_VERSION_REVISION & 0xff) };
+
static std::string _trimString(const std::string &s)
{
unsigned long end = (unsigned long)s.length();
@@ -191,10 +202,10 @@ static void _networkToJson(nlohmann::json &nj,const ZT_VirtualNetworkConfig *nc,
case ZT_NETWORK_TYPE_PUBLIC: ntype = "PUBLIC"; break;
}
- Utils::snprintf(tmp,sizeof(tmp),"%.16llx",nc->nwid);
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",nc->nwid);
nj["id"] = tmp;
nj["nwid"] = tmp;
- Utils::snprintf(tmp,sizeof(tmp),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(unsigned int)((nc->mac >> 40) & 0xff),(unsigned int)((nc->mac >> 32) & 0xff),(unsigned int)((nc->mac >> 24) & 0xff),(unsigned int)((nc->mac >> 16) & 0xff),(unsigned int)((nc->mac >> 8) & 0xff),(unsigned int)(nc->mac & 0xff));
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(unsigned int)((nc->mac >> 40) & 0xff),(unsigned int)((nc->mac >> 32) & 0xff),(unsigned int)((nc->mac >> 24) & 0xff),(unsigned int)((nc->mac >> 16) & 0xff),(unsigned int)((nc->mac >> 8) & 0xff),(unsigned int)(nc->mac & 0xff));
nj["mac"] = tmp;
nj["name"] = nc->name;
nj["status"] = nstatus;
@@ -212,16 +223,16 @@ static void _networkToJson(nlohmann::json &nj,const ZT_VirtualNetworkConfig *nc,
nlohmann::json aa = nlohmann::json::array();
for(unsigned int i=0;i<nc->assignedAddressCount;++i) {
- aa.push_back(reinterpret_cast<const InetAddress *>(&(nc->assignedAddresses[i]))->toString());
+ aa.push_back(reinterpret_cast<const InetAddress *>(&(nc->assignedAddresses[i]))->toString(tmp));
}
nj["assignedAddresses"] = aa;
nlohmann::json ra = nlohmann::json::array();
for(unsigned int i=0;i<nc->routeCount;++i) {
nlohmann::json rj;
- rj["target"] = reinterpret_cast<const InetAddress *>(&(nc->routes[i].target))->toString();
+ rj["target"] = reinterpret_cast<const InetAddress *>(&(nc->routes[i].target))->toString(tmp);
if (nc->routes[i].via.ss_family == nc->routes[i].target.ss_family)
- rj["via"] = reinterpret_cast<const InetAddress *>(&(nc->routes[i].via))->toIpString();
+ rj["via"] = reinterpret_cast<const InetAddress *>(&(nc->routes[i].via))->toIpString(tmp);
else rj["via"] = nlohmann::json();
rj["flags"] = (int)nc->routes[i].flags;
rj["metric"] = (int)nc->routes[i].metric;
@@ -241,24 +252,25 @@ static void _peerToJson(nlohmann::json &pj,const ZT_Peer *peer)
case ZT_PEER_ROLE_PLANET: prole = "PLANET"; break;
}
- Utils::snprintf(tmp,sizeof(tmp),"%.10llx",peer->address);
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.10llx",peer->address);
pj["address"] = tmp;
pj["versionMajor"] = peer->versionMajor;
pj["versionMinor"] = peer->versionMinor;
pj["versionRev"] = peer->versionRev;
- Utils::snprintf(tmp,sizeof(tmp),"%d.%d.%d",peer->versionMajor,peer->versionMinor,peer->versionRev);
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%d.%d.%d",peer->versionMajor,peer->versionMinor,peer->versionRev);
pj["version"] = tmp;
pj["latency"] = peer->latency;
pj["role"] = prole;
nlohmann::json pa = nlohmann::json::array();
for(unsigned int i=0;i<peer->pathCount;++i) {
+ int64_t lastSend = peer->paths[i].lastSend;
+ int64_t lastReceive = peer->paths[i].lastReceive;
nlohmann::json j;
- j["address"] = reinterpret_cast<const InetAddress *>(&(peer->paths[i].address))->toString();
- j["lastSend"] = peer->paths[i].lastSend;
- j["lastReceive"] = peer->paths[i].lastReceive;
+ j["address"] = reinterpret_cast<const InetAddress *>(&(peer->paths[i].address))->toString(tmp);
+ j["lastSend"] = (lastSend < 0) ? 0 : lastSend;
+ j["lastReceive"] = (lastReceive < 0) ? 0 : lastReceive;
j["trustedPathId"] = peer->paths[i].trustedPathId;
- j["linkQuality"] = (double)peer->paths[i].linkQuality / (double)ZT_PATH_LINK_QUALITY_MAX;
j["active"] = (bool)(peer->paths[i].expired == 0);
j["expired"] = (bool)(peer->paths[i].expired != 0);
j["preferred"] = (bool)(peer->paths[i].preferred != 0);
@@ -269,19 +281,19 @@ static void _peerToJson(nlohmann::json &pj,const ZT_Peer *peer)
static void _moonToJson(nlohmann::json &mj,const World &world)
{
- char tmp[64];
- Utils::snprintf(tmp,sizeof(tmp),"%.16llx",world.id());
+ char tmp[4096];
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",world.id());
mj["id"] = tmp;
mj["timestamp"] = world.timestamp();
- mj["signature"] = Utils::hex(world.signature().data,(unsigned int)world.signature().size());
- mj["updatesMustBeSignedBy"] = Utils::hex(world.updatesMustBeSignedBy().data,(unsigned int)world.updatesMustBeSignedBy().size());
+ mj["signature"] = Utils::hex(world.signature().data,ZT_C25519_SIGNATURE_LEN,tmp);
+ mj["updatesMustBeSignedBy"] = Utils::hex(world.updatesMustBeSignedBy().data,ZT_C25519_PUBLIC_KEY_LEN,tmp);
nlohmann::json ra = nlohmann::json::array();
for(std::vector<World::Root>::const_iterator r(world.roots().begin());r!=world.roots().end();++r) {
nlohmann::json rj;
- rj["identity"] = r->identity.toString(false);
+ rj["identity"] = r->identity.toString(false,tmp);
nlohmann::json eps = nlohmann::json::array();
for(std::vector<InetAddress>::const_iterator a(r->stableEndpoints.begin());a!=r->stableEndpoints.end();++a)
- eps.push_back(a->toString());
+ eps.push_back(a->toString(tmp));
rj["stableEndpoints"] = eps;
ra.push_back(rj);
}
@@ -293,18 +305,12 @@ class OneServiceImpl;
static int SnodeVirtualNetworkConfigFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t nwid,void **nuptr,enum ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nwconf);
static void SnodeEventCallback(ZT_Node *node,void *uptr,void *tptr,enum ZT_Event event,const void *metaData);
-static long SnodeDataStoreGetFunction(ZT_Node *node,void *uptr,void *tptr,const char *name,void *buf,unsigned long bufSize,unsigned long readIndex,unsigned long *totalSize);
-static int SnodeDataStorePutFunction(ZT_Node *node,void *uptr,void *tptr,const char *name,const void *data,unsigned long len,int secure);
-static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,void *tptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl);
+static void SnodeStatePutFunction(ZT_Node *node,void *uptr,void *tptr,enum ZT_StateObjectType type,const uint64_t id[2],const void *data,int len);
+static int SnodeStateGetFunction(ZT_Node *node,void *uptr,void *tptr,enum ZT_StateObjectType type,const uint64_t id[2],void *data,unsigned int maxlen);
+static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,void *tptr,int64_t localSocket,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl);
static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len);
-static int SnodePathCheckFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr);
+static int SnodePathCheckFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,int64_t localSocket,const struct sockaddr_storage *remoteAddr);
static int SnodePathLookupFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,int family,struct sockaddr_storage *result);
-
-#ifdef ZT_ENABLE_CLUSTER
-static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len);
-static int SclusterGeoIpFunction(void *uptr,const struct sockaddr_storage *addr,int *x,int *y,int *z);
-#endif
-
static void StapFrameHandler(void *uptr,void *tptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len);
static int ShttpOnMessageBegin(http_parser *parser);
@@ -343,37 +349,37 @@ static const struct http_parser_settings HTTP_PARSER_SETTINGS = {
};
#endif
+/**
+ * A TCP connection and related state and buffers
+ */
struct TcpConnection
{
enum {
+ TCP_UNCATEGORIZED_INCOMING, // uncategorized incoming connection
TCP_HTTP_INCOMING,
- TCP_HTTP_OUTGOING, // not currently used
- TCP_TUNNEL_OUTGOING // fale-SSL outgoing tunnel -- HTTP-related fields are not used
+ TCP_HTTP_OUTGOING,
+ TCP_TUNNEL_OUTGOING // TUNNELED mode proxy outbound connection
} type;
- bool shouldKeepAlive;
OneServiceImpl *parent;
PhySocket *sock;
- InetAddress from;
+ InetAddress remoteAddr;
+ uint64_t lastReceive;
+
+ // Used for inbound HTTP connections
http_parser parser;
unsigned long messageSize;
- uint64_t lastActivity;
-
std::string currentHeaderField;
std::string currentHeaderValue;
-
std::string url;
std::string status;
std::map< std::string,std::string > headers;
- std::string body;
- std::string writeBuf;
- Mutex writeBuf_m;
+ std::string readq;
+ std::string writeq;
+ Mutex writeq_m;
};
-// Used to pseudo-randomize local source port picking
-static volatile unsigned int _udpPortPickerCounter = 0;
-
class OneServiceImpl : public OneService
{
public:
@@ -382,14 +388,21 @@ public:
const std::string _homePath;
std::string _authToken;
std::string _controllerDbPath;
+ const std::string _networksPath;
+ const std::string _moonsPath;
+
EmbeddedNetworkController *_controller;
Phy<OneServiceImpl *> _phy;
Node *_node;
SoftwareUpdater *_updater;
+ PhySocket *_localControlSocket4;
+ PhySocket *_localControlSocket6;
bool _updateAutoApply;
+ bool _allowTcpFallbackRelay;
unsigned int _primaryPort;
+ volatile unsigned int _udpPortPickerCounter;
- // Local configuration and memo-ized static path definitions
+ // Local configuration and memo-ized information from it
json _localConfig;
Hashtable< uint64_t,std::vector<InetAddress> > _v4Hints;
Hashtable< uint64_t,std::vector<InetAddress> > _v6Hints;
@@ -412,13 +425,8 @@ public:
* destructively with uPnP port mapping behavior in very weird buggy ways.
* It's only used if uPnP/NAT-PMP is enabled in this build.
*/
- Binder _bindings[3];
unsigned int _ports[3];
- uint16_t _portsBE[3]; // ports in big-endian network byte order as in sockaddr
-
- // Sockets for JSON API -- bound only to V4 and V6 localhost
- PhySocket *_v4TcpControlSocket;
- PhySocket *_v6TcpControlSocket;
+ Binder _binder;
// Time we last received a packet from a global address
uint64_t _lastDirectReceiveFromGlobal;
@@ -430,7 +438,7 @@ public:
uint64_t _lastRestart;
// Deadline for the next background task service function
- volatile uint64_t _nextBackgroundTaskDeadline;
+ volatile int64_t _nextBackgroundTaskDeadline;
// Configured networks
struct NetworkState
@@ -454,7 +462,8 @@ public:
Mutex _nets_m;
// Active TCP/IP connections
- std::set< TcpConnection * > _tcpConnections; // no mutex for this since it's done in the main loop thread only
+ std::vector< TcpConnection * > _tcpConnections;
+ Mutex _tcpConnections_m;
TcpConnection *_tcpFallbackTunnel;
// Termination status information
@@ -468,13 +477,6 @@ public:
PortMapper *_portMapper;
#endif
- // Cluster management instance if enabled
-#ifdef ZT_ENABLE_CLUSTER
- PhySocket *_clusterMessageSocket;
- ClusterDefinition *_clusterDefinition;
- unsigned int _clusterMemberId;
-#endif
-
// Set to false to force service to stop
volatile bool _run;
Mutex _run_m;
@@ -483,15 +485,18 @@ public:
OneServiceImpl(const char *hp,unsigned int port) :
_homePath((hp) ? hp : ".")
- ,_controllerDbPath(_homePath + ZT_PATH_SEPARATOR_S ZT_CONTROLLER_DB_PATH)
+ ,_controllerDbPath(_homePath + ZT_PATH_SEPARATOR_S "controller.d")
+ ,_networksPath(_homePath + ZT_PATH_SEPARATOR_S "networks.d")
+ ,_moonsPath(_homePath + ZT_PATH_SEPARATOR_S "moons.d")
,_controller((EmbeddedNetworkController *)0)
,_phy(this,false,true)
,_node((Node *)0)
,_updater((SoftwareUpdater *)0)
+ ,_localControlSocket4((PhySocket *)0)
+ ,_localControlSocket6((PhySocket *)0)
,_updateAutoApply(false)
,_primaryPort(port)
- ,_v4TcpControlSocket((PhySocket *)0)
- ,_v6TcpControlSocket((PhySocket *)0)
+ ,_udpPortPickerCounter(0)
,_lastDirectReceiveFromGlobal(0)
#ifdef ZT_TCP_FALLBACK_RELAY
,_lastSendToGlobalV4(0)
@@ -504,11 +509,6 @@ public:
#ifdef ZT_USE_MINIUPNPC
,_portMapper((PortMapper *)0)
#endif
-#ifdef ZT_ENABLE_CLUSTER
- ,_clusterMessageSocket((PhySocket *)0)
- ,_clusterDefinition((ClusterDefinition *)0)
- ,_clusterMemberId(0)
-#endif
,_run(true)
{
_ports[0] = 0;
@@ -518,23 +518,13 @@ public:
virtual ~OneServiceImpl()
{
- for(int i=0;i<3;++i)
- _bindings[i].closeAll(_phy);
-
- _phy.close(_v4TcpControlSocket);
- _phy.close(_v6TcpControlSocket);
-
-#ifdef ZT_ENABLE_CLUSTER
- _phy.close(_clusterMessageSocket);
-#endif
-
+ _binder.closeAll(_phy);
+ _phy.close(_localControlSocket4);
+ _phy.close(_localControlSocket6);
#ifdef ZT_USE_MINIUPNPC
delete _portMapper;
#endif
delete _controller;
-#ifdef ZT_ENABLE_CLUSTER
- delete _clusterDefinition;
-#endif
}
virtual ReasonForTermination run()
@@ -560,15 +550,11 @@ public:
_authToken = _trimString(_authToken);
}
- // Clean up any legacy files if present
- OSUtils::rm((_homePath + ZT_PATH_SEPARATOR_S "peers.save").c_str());
- OSUtils::rm((_homePath + ZT_PATH_SEPARATOR_S "world").c_str());
-
{
struct ZT_Node_Callbacks cb;
cb.version = 0;
- cb.dataStoreGetFunction = SnodeDataStoreGetFunction;
- cb.dataStorePutFunction = SnodeDataStorePutFunction;
+ cb.stateGetFunction = SnodeStateGetFunction;
+ cb.statePutFunction = SnodeStatePutFunction;
cb.wirePacketSendFunction = SnodeWirePacketSendFunction;
cb.virtualNetworkFrameFunction = SnodeVirtualNetworkFrameFunction;
cb.virtualNetworkConfigFunction = SnodeVirtualNetworkConfigFunction;
@@ -579,16 +565,16 @@ public:
}
// Read local configuration
+ std::vector<InetAddress> explicitBind;
{
- uint64_t trustedPathIds[ZT_MAX_TRUSTED_PATHS];
- InetAddress trustedPathNetworks[ZT_MAX_TRUSTED_PATHS];
- unsigned int trustedPathCount = 0;
+ std::map<InetAddress,ZT_PhysicalPathConfiguration> ppc;
- // Old style "trustedpaths" flat file -- will eventually go away
+ // LEGACY: support old "trustedpaths" flat file
FILE *trustpaths = fopen((_homePath + ZT_PATH_SEPARATOR_S "trustedpaths").c_str(),"r");
if (trustpaths) {
+ fprintf(stderr,"WARNING: 'trustedpaths' flat file format is deprecated in favor of path definitions in local.conf" ZT_EOL_S);
char buf[1024];
- while ((fgets(buf,sizeof(buf),trustpaths))&&(trustedPathCount < ZT_MAX_TRUSTED_PATHS)) {
+ while (fgets(buf,sizeof(buf),trustpaths)) {
int fno = 0;
char *saveptr = (char *)0;
uint64_t trustedPathId = 0;
@@ -601,10 +587,9 @@ public:
} else break;
++fno;
}
- if ( (trustedPathId != 0) && ((trustedPathNetwork.ss_family == AF_INET)||(trustedPathNetwork.ss_family == AF_INET6)) && (trustedPathNetwork.ipScope() != InetAddress::IP_SCOPE_GLOBAL) && (trustedPathNetwork.netmaskBits() > 0) ) {
- trustedPathIds[trustedPathCount] = trustedPathId;
- trustedPathNetworks[trustedPathCount] = trustedPathNetwork;
- ++trustedPathCount;
+ if ( (trustedPathId != 0) && ((trustedPathNetwork.ss_family == AF_INET)||(trustedPathNetwork.ss_family == AF_INET6)) && (trustedPathNetwork.netmaskBits() > 0) ) {
+ ppc[trustedPathNetwork].trustedPathId = trustedPathId;
+ ppc[trustedPathNetwork].mtu = 0; // use default
}
}
fclose(trustpaths);
@@ -628,29 +613,52 @@ public:
json &physical = _localConfig["physical"];
if (physical.is_object()) {
for(json::iterator phy(physical.begin());phy!=physical.end();++phy) {
- InetAddress net(OSUtils::jsonString(phy.key(),""));
+ InetAddress net(OSUtils::jsonString(phy.key(),"").c_str());
if (net) {
if (phy.value().is_object()) {
uint64_t tpid;
if ((tpid = OSUtils::jsonInt(phy.value()["trustedPathId"],0ULL)) != 0ULL) {
- if ( ((net.ss_family == AF_INET)||(net.ss_family == AF_INET6)) && (trustedPathCount < ZT_MAX_TRUSTED_PATHS) && (net.ipScope() != InetAddress::IP_SCOPE_GLOBAL) && (net.netmaskBits() > 0) ) {
- trustedPathIds[trustedPathCount] = tpid;
- trustedPathNetworks[trustedPathCount] = net;
- ++trustedPathCount;
- }
+ if ((net.ss_family == AF_INET)||(net.ss_family == AF_INET6))
+ ppc[net].trustedPathId = tpid;
}
+ ppc[net].mtu = (int)OSUtils::jsonInt(phy.value()["mtu"],0ULL); // 0 means use default
+ }
+ }
+ }
+ }
+
+ json &settings = _localConfig["settings"];
+ if (settings.is_object()) {
+ // Allow controller DB path to be put somewhere else
+ const std::string cdbp(OSUtils::jsonString(settings["controllerDbPath"],""));
+ if (cdbp.length() > 0)
+ _controllerDbPath = cdbp;
+
+ // Bind to wildcard instead of to specific interfaces (disables full tunnel capability)
+ json &bind = settings["bind"];
+ if (bind.is_array()) {
+ for(unsigned long i=0;i<bind.size();++i) {
+ const std::string ips(OSUtils::jsonString(bind[i],""));
+ if (ips.length() > 0) {
+ InetAddress ip(ips.c_str());
+ if ((ip.ss_family == AF_INET)||(ip.ss_family == AF_INET6))
+ explicitBind.push_back(ip);
}
}
}
}
// Set trusted paths if there are any
- if (trustedPathCount)
- _node->setTrustedPaths(reinterpret_cast<const struct sockaddr_storage *>(trustedPathNetworks),trustedPathIds,trustedPathCount);
+ if (ppc.size() > 0) {
+ for(std::map<InetAddress,ZT_PhysicalPathConfiguration>::iterator i(ppc.begin());i!=ppc.end();++i)
+ _node->setPhysicalPathConfiguration(reinterpret_cast<const struct sockaddr_storage *>(&(i->first)),&(i->second));
+ }
}
+
+ // Apply other runtime configuration from local.conf
applyLocalConfig();
- // Bind TCP control socket
+ // Make sure we can use the primary port, and hunt for one if configured to do so
const int portTrials = (_primaryPort == 0) ? 256 : 1; // if port is 0, pick random
for(int k=0;k<portTrials;++k) {
if (_primaryPort == 0) {
@@ -658,35 +666,8 @@ public:
Utils::getSecureRandom(&randp,sizeof(randp));
_primaryPort = 20000 + (randp % 45500);
}
-
if (_trialBind(_primaryPort)) {
- struct sockaddr_in in4;
- memset(&in4,0,sizeof(in4));
- in4.sin_family = AF_INET;
- in4.sin_addr.s_addr = Utils::hton((uint32_t)((_allowManagementFrom.size() > 0) ? 0 : 0x7f000001)); // right now we just listen for TCP @127.0.0.1
- in4.sin_port = Utils::hton((uint16_t)_primaryPort);
- _v4TcpControlSocket = _phy.tcpListen((const struct sockaddr *)&in4,this);
-
- struct sockaddr_in6 in6;
- memset((void *)&in6,0,sizeof(in6));
- in6.sin6_family = AF_INET6;
- in6.sin6_port = in4.sin_port;
- if (_allowManagementFrom.size() == 0)
- in6.sin6_addr.s6_addr[15] = 1; // IPv6 localhost == ::1
- _v6TcpControlSocket = _phy.tcpListen((const struct sockaddr *)&in6,this);
-
- // We must bind one of IPv4 or IPv6 -- support either failing to support hosts that
- // have only IPv4 or only IPv6 stacks.
- if ((_v4TcpControlSocket)||(_v6TcpControlSocket)) {
- _ports[0] = _primaryPort;
- break;
- } else {
- if (_v4TcpControlSocket)
- _phy.close(_v4TcpControlSocket,false);
- if (_v6TcpControlSocket)
- _phy.close(_v6TcpControlSocket,false);
- _primaryPort = 0;
- }
+ _ports[0] = _primaryPort;
} else {
_primaryPort = 0;
}
@@ -698,15 +679,31 @@ public:
return _termReason;
}
- // Write file containing primary port to be read by CLIs, etc.
+ // Bind TCP control socket to 127.0.0.1 and ::1 as well for loopback TCP control socket queries
+ {
+ struct sockaddr_in lo4;
+ memset(&lo4,0,sizeof(lo4));
+ lo4.sin_family = AF_INET;
+ lo4.sin_addr.s_addr = Utils::hton((uint32_t)0x7f000001);
+ lo4.sin_port = Utils::hton((uint16_t)_ports[0]);
+ _localControlSocket4 = _phy.tcpListen((const struct sockaddr *)&lo4);
+ struct sockaddr_in6 lo6;
+ memset(&lo6,0,sizeof(lo6));
+ lo6.sin6_family = AF_INET6;
+ lo6.sin6_addr.s6_addr[15] = 1;
+ lo6.sin6_port = lo4.sin_port;
+ _localControlSocket6 = _phy.tcpListen((const struct sockaddr *)&lo6);
+ }
+
+ // Save primary port to a file so CLIs and GUIs can learn it easily
char portstr[64];
- Utils::snprintf(portstr,sizeof(portstr),"%u",_ports[0]);
+ OSUtils::ztsnprintf(portstr,sizeof(portstr),"%u",_ports[0]);
OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S "zerotier-one.port").c_str(),std::string(portstr));
// Attempt to bind to a secondary port chosen from our ZeroTier address.
// This exists because there are buggy NATs out there that fail if more
// than one device behind the same NAT tries to use the same internal
- // private address port number.
+ // private address port number. Buggy NATs are a running theme.
_ports[1] = 20000 + ((unsigned int)_node->address() % 45500);
for(int i=0;;++i) {
if (i > 1000) {
@@ -738,70 +735,22 @@ public:
}
if (_ports[2]) {
char uniqueName[64];
- Utils::snprintf(uniqueName,sizeof(uniqueName),"ZeroTier/%.10llx@%u",_node->address(),_ports[2]);
+ OSUtils::ztsnprintf(uniqueName,sizeof(uniqueName),"ZeroTier/%.10llx@%u",_node->address(),_ports[2]);
_portMapper = new PortMapper(_ports[2],uniqueName);
}
}
}
#endif
- // Populate ports in big-endian format for quick compare
- for(int i=0;i<3;++i)
- _portsBE[i] = Utils::hton((uint16_t)_ports[i]);
+ // Delete legacy iddb.d if present (cleanup)
+ OSUtils::rmDashRf((_homePath + ZT_PATH_SEPARATOR_S "iddb.d").c_str());
+ // Network controller is now enabled by default for desktop and server
_controller = new EmbeddedNetworkController(_node,_controllerDbPath.c_str());
_node->setNetconfMaster((void *)_controller);
-#ifdef ZT_ENABLE_CLUSTER
- if (OSUtils::fileExists((_homePath + ZT_PATH_SEPARATOR_S "cluster").c_str())) {
- _clusterDefinition = new ClusterDefinition(_node->address(),(_homePath + ZT_PATH_SEPARATOR_S "cluster").c_str());
- if (_clusterDefinition->size() > 0) {
- std::vector<ClusterDefinition::MemberDefinition> members(_clusterDefinition->members());
- for(std::vector<ClusterDefinition::MemberDefinition>::iterator m(members.begin());m!=members.end();++m) {
- PhySocket *cs = _phy.udpBind(reinterpret_cast<const struct sockaddr *>(&(m->clusterEndpoint)));
- if (cs) {
- if (_clusterMessageSocket) {
- _phy.close(_clusterMessageSocket,false);
- _phy.close(cs,false);
-
- Mutex::Lock _l(_termReason_m);
- _termReason = ONE_UNRECOVERABLE_ERROR;
- _fatalErrorMessage = "cluster: can't determine my cluster member ID: able to bind more than one cluster message socket IP/port!";
- return _termReason;
- }
- _clusterMessageSocket = cs;
- _clusterMemberId = m->id;
- }
- }
-
- if (!_clusterMessageSocket) {
- Mutex::Lock _l(_termReason_m);
- _termReason = ONE_UNRECOVERABLE_ERROR;
- _fatalErrorMessage = "cluster: can't determine my cluster member ID: unable to bind to any cluster message socket IP/port.";
- return _termReason;
- }
-
- const ClusterDefinition::MemberDefinition &me = (*_clusterDefinition)[_clusterMemberId];
- InetAddress endpoints[255];
- unsigned int numEndpoints = 0;
- for(std::vector<InetAddress>::const_iterator i(me.zeroTierEndpoints.begin());i!=me.zeroTierEndpoints.end();++i)
- endpoints[numEndpoints++] = *i;
-
- if (_node->clusterInit(_clusterMemberId,reinterpret_cast<const struct sockaddr_storage *>(endpoints),numEndpoints,me.x,me.y,me.z,&SclusterSendFunction,this,_clusterDefinition->geo().available() ? &SclusterGeoIpFunction : 0,this) == ZT_RESULT_OK) {
- std::vector<ClusterDefinition::MemberDefinition> members(_clusterDefinition->members());
- for(std::vector<ClusterDefinition::MemberDefinition>::iterator m(members.begin());m!=members.end();++m) {
- if (m->id != _clusterMemberId)
- _node->clusterAddMember(m->id);
- }
- }
- } else {
- delete _clusterDefinition;
- _clusterDefinition = (ClusterDefinition *)0;
- }
- }
-#endif
-
- { // Load existing networks
+ // Join existing networks in networks.d
+ {
std::vector<std::string> networksDotD(OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S "networks.d").c_str()));
for(std::vector<std::string>::iterator f(networksDotD.begin());f!=networksDotD.end();++f) {
std::size_t dot = f->find_last_of('.');
@@ -809,7 +758,9 @@ public:
_node->join(Utils::hexStrToU64(f->substr(0,dot).c_str()),(void *)0,(void *)0);
}
}
- { // Load existing moons
+
+ // Orbit existing moons in moons.d
+ {
std::vector<std::string> moonsDotD(OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S "moons.d").c_str()));
for(std::vector<std::string>::iterator f(moonsDotD.begin());f!=moonsDotD.end();++f) {
std::size_t dot = f->find_last_of('.');
@@ -818,14 +769,15 @@ public:
}
}
+ // Main I/O loop
_nextBackgroundTaskDeadline = 0;
- uint64_t clockShouldBe = OSUtils::now();
+ int64_t clockShouldBe = OSUtils::now();
_lastRestart = clockShouldBe;
- uint64_t lastTapMulticastGroupCheck = 0;
- uint64_t lastBindRefresh = 0;
- uint64_t lastUpdateCheck = clockShouldBe;
- uint64_t lastLocalInterfaceAddressCheck = (clockShouldBe - ZT_LOCAL_INTERFACE_CHECK_INTERVAL) + 15000; // do this in 15s to give portmapper time to configure and other things time to settle
- uint64_t lastCleanedIddb = 0;
+ int64_t lastTapMulticastGroupCheck = 0;
+ int64_t lastBindRefresh = 0;
+ int64_t lastUpdateCheck = clockShouldBe;
+ int64_t lastCleanedPeersDb = 0;
+ int64_t lastLocalInterfaceAddressCheck = (clockShouldBe - ZT_LOCAL_INTERFACE_CHECK_INTERVAL) + 15000; // do this in 15s to give portmapper time to configure and other things time to settle
for(;;) {
_run_m.lock();
if (!_run) {
@@ -838,13 +790,7 @@ public:
_run_m.unlock();
}
- const uint64_t now = OSUtils::now();
-
- // Clean iddb.d on start and every 24 hours
- if ((now - lastCleanedIddb) > 86400000) {
- lastCleanedIddb = now;
- OSUtils::cleanDirectory((_homePath + ZT_PATH_SEPARATOR_S "iddb.d").c_str(),now - ZT_IDDB_CLEANUP_AGE);
- }
+ const int64_t now = OSUtils::now();
// Attempt to detect sleep/wake events by detecting delay overruns
bool restarted = false;
@@ -863,11 +809,13 @@ public:
// Refresh bindings in case device's interfaces have changed, and also sync routes to update any shadow routes (e.g. shadow default)
if (((now - lastBindRefresh) >= ZT_BINDER_REFRESH_PERIOD)||(restarted)) {
lastBindRefresh = now;
+ unsigned int p[3];
+ unsigned int pc = 0;
for(int i=0;i<3;++i) {
- if (_ports[i]) {
- _bindings[i].refresh(_phy,_ports[i],*this);
- }
+ if (_ports[i])
+ p[pc++] = _ports[i];
}
+ _binder.refresh(_phy,p,pc,explicitBind,*this);
{
Mutex::Lock _l(_nets_m);
for(std::map<uint64_t,NetworkState>::iterator n(_nets.begin());n!=_nets.end();++n) {
@@ -877,30 +825,40 @@ public:
}
}
- uint64_t dl = _nextBackgroundTaskDeadline;
+ // Run background task processor in core if it's time to do so
+ int64_t dl = _nextBackgroundTaskDeadline;
if (dl <= now) {
_node->processBackgroundTasks((void *)0,now,&_nextBackgroundTaskDeadline);
dl = _nextBackgroundTaskDeadline;
}
+ // Close TCP fallback tunnel if we have direct UDP
if ((_tcpFallbackTunnel)&&((now - _lastDirectReceiveFromGlobal) < (ZT_TCP_FALLBACK_AFTER / 2)))
_phy.close(_tcpFallbackTunnel->sock);
+ // Sync multicast group memberships
if ((now - lastTapMulticastGroupCheck) >= ZT_TAP_CHECK_MULTICAST_INTERVAL) {
lastTapMulticastGroupCheck = now;
- Mutex::Lock _l(_nets_m);
- for(std::map<uint64_t,NetworkState>::const_iterator n(_nets.begin());n!=_nets.end();++n) {
- if (n->second.tap) {
- std::vector<MulticastGroup> added,removed;
- n->second.tap->scanMulticastGroups(added,removed);
- for(std::vector<MulticastGroup>::iterator m(added.begin());m!=added.end();++m)
- _node->multicastSubscribe((void *)0,n->first,m->mac().toInt(),m->adi());
- for(std::vector<MulticastGroup>::iterator m(removed.begin());m!=removed.end();++m)
- _node->multicastUnsubscribe(n->first,m->mac().toInt(),m->adi());
+ std::vector< std::pair< uint64_t,std::pair< std::vector<MulticastGroup>,std::vector<MulticastGroup> > > > mgChanges;
+ {
+ Mutex::Lock _l(_nets_m);
+ mgChanges.reserve(_nets.size() + 1);
+ for(std::map<uint64_t,NetworkState>::const_iterator n(_nets.begin());n!=_nets.end();++n) {
+ if (n->second.tap) {
+ mgChanges.push_back(std::pair< uint64_t,std::pair< std::vector<MulticastGroup>,std::vector<MulticastGroup> > >(n->first,std::pair< std::vector<MulticastGroup>,std::vector<MulticastGroup> >()));
+ n->second.tap->scanMulticastGroups(mgChanges.back().second.first,mgChanges.back().second.second);
+ }
}
}
+ for(std::vector< std::pair< uint64_t,std::pair< std::vector<MulticastGroup>,std::vector<MulticastGroup> > > >::iterator c(mgChanges.begin());c!=mgChanges.end();++c) {
+ for(std::vector<MulticastGroup>::iterator m(c->second.first.begin());m!=c->second.first.end();++m)
+ _node->multicastSubscribe((void *)0,c->first,m->mac().toInt(),m->adi());
+ for(std::vector<MulticastGroup>::iterator m(c->second.second.begin());m!=c->second.second.end();++m)
+ _node->multicastUnsubscribe(c->first,m->mac().toInt(),m->adi());
+ }
}
+ // Sync information about physical network interfaces
if ((now - lastLocalInterfaceAddressCheck) >= ZT_LOCAL_INTERFACE_CHECK_INTERVAL) {
lastLocalInterfaceAddressCheck = now;
@@ -914,26 +872,33 @@ public:
}
#endif
- std::vector<InetAddress> boundAddrs(_bindings[0].allBoundLocalInterfaceAddresses());
+ std::vector<InetAddress> boundAddrs(_binder.allBoundLocalInterfaceAddresses());
for(std::vector<InetAddress>::const_iterator i(boundAddrs.begin());i!=boundAddrs.end();++i)
_node->addLocalInterfaceAddress(reinterpret_cast<const struct sockaddr_storage *>(&(*i)));
}
+ // Clean peers.d periodically
+ if ((now - lastCleanedPeersDb) >= 3600000) {
+ lastCleanedPeersDb = now;
+ OSUtils::cleanDirectory((_homePath + ZT_PATH_SEPARATOR_S "peers.d").c_str(),now - 2592000000LL); // delete older than 30 days
+ }
+
const unsigned long delay = (dl > now) ? (unsigned long)(dl - now) : 100;
clockShouldBe = now + (uint64_t)delay;
_phy.poll(delay);
}
- } catch (std::exception &exc) {
+ } catch (std::exception &e) {
Mutex::Lock _l(_termReason_m);
_termReason = ONE_UNRECOVERABLE_ERROR;
- _fatalErrorMessage = exc.what();
+ _fatalErrorMessage = std::string("unexpected exception in main thread: ")+e.what();
} catch ( ... ) {
Mutex::Lock _l(_termReason_m);
_termReason = ONE_UNRECOVERABLE_ERROR;
- _fatalErrorMessage = "unexpected exception in main thread";
+ _fatalErrorMessage = "unexpected exception in main thread: unknown exception";
}
try {
+ Mutex::Lock _l(_tcpConnections_m);
while (!_tcpConnections.empty())
_phy.close((*_tcpConnections.begin())->sock);
} catch ( ... ) {}
@@ -974,6 +939,47 @@ public:
else return std::string();
}
+#ifdef ZT_SDK
+ virtual void leave(const uint64_t hp)
+ {
+ _node->leave(hp, NULL, NULL);
+ }
+
+ virtual void join(const uint64_t hp)
+ {
+ _node->join(hp, NULL, NULL);
+ }
+
+ virtual std::string givenHomePath()
+ {
+ return _homePath;
+ }
+
+ std::vector<ZT_VirtualNetworkRoute> *getRoutes(uint64_t nwid)
+ {
+ Mutex::Lock _l(_nets_m);
+ NetworkState &n = _nets[nwid];
+ std::vector<ZT_VirtualNetworkRoute> *routes = new std::vector<ZT_VirtualNetworkRoute>();
+ for(int i=0; i<ZT_MAX_NETWORK_ROUTES; i++) {
+ routes->push_back(n.config.routes[i]);
+ }
+ return routes;
+ }
+
+ virtual Node *getNode()
+ {
+ return _node;
+ }
+
+ virtual void removeNets()
+ {
+ Mutex::Lock _l(_nets_m);
+ std::map<uint64_t,NetworkState>::iterator i;
+ for(i = _nets.begin(); i != _nets.end(); i++)
+ delete i->second.tap;
+ }
+#endif // ZT_SDK
+
virtual void terminate()
{
_run_m.lock();
@@ -1001,8 +1007,8 @@ public:
return false;
n->second.settings = settings;
- char nlcpath[256];
- Utils::snprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_homePath.c_str(),nwid);
+ char nlcpath[4096];
+ OSUtils::ztsnprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_networksPath.c_str(),nwid);
FILE *out = fopen(nlcpath,"w");
if (out) {
fprintf(out,"allowManaged=%d\n",(int)n->second.settings.allowManaged);
@@ -1017,7 +1023,9 @@ public:
return true;
}
- // Internal implementation methods -----------------------------------------
+ // =========================================================================
+ // Internal implementation methods for control plane, route setup, etc.
+ // =========================================================================
inline unsigned int handleControlPlaneHttpRequest(
const InetAddress &fromAddress,
@@ -1119,7 +1127,7 @@ public:
ZT_NodeStatus status;
_node->status(&status);
- Utils::snprintf(tmp,sizeof(tmp),"%.10llx",status.address);
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.10llx",status.address);
res["address"] = tmp;
res["publicIdentity"] = status.publicIdentity;
res["online"] = (bool)(status.online != 0);
@@ -1128,7 +1136,7 @@ public:
res["versionMinor"] = ZEROTIER_ONE_VERSION_MINOR;
res["versionRev"] = ZEROTIER_ONE_VERSION_REVISION;
res["versionBuild"] = ZEROTIER_ONE_VERSION_BUILD;
- Utils::snprintf(tmp,sizeof(tmp),"%d.%d.%d",ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION);
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%d.%d.%d",ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION);
res["version"] = tmp;
res["clock"] = OSUtils::now();
@@ -1138,45 +1146,20 @@ public:
}
json &settings = res["config"]["settings"];
settings["primaryPort"] = OSUtils::jsonInt(settings["primaryPort"],(uint64_t)_primaryPort) & 0xffff;
+ settings["allowTcpFallbackRelay"] = OSUtils::jsonBool(settings["allowTcpFallbackRelay"],_allowTcpFallbackRelay);
#ifdef ZT_USE_MINIUPNPC
settings["portMappingEnabled"] = OSUtils::jsonBool(settings["portMappingEnabled"],true);
#else
settings["portMappingEnabled"] = false; // not supported in build
#endif
+#ifndef ZT_SDK
settings["softwareUpdate"] = OSUtils::jsonString(settings["softwareUpdate"],ZT_SOFTWARE_UPDATE_DEFAULT);
settings["softwareUpdateChannel"] = OSUtils::jsonString(settings["softwareUpdateChannel"],ZT_SOFTWARE_UPDATE_DEFAULT_CHANNEL);
-
+#endif
const World planet(_node->planet());
res["planetWorldId"] = planet.id();
res["planetWorldTimestamp"] = planet.timestamp();
-#ifdef ZT_ENABLE_CLUSTER
- json cj;
- ZT_ClusterStatus cs;
- _node->clusterStatus(&cs);
- if (cs.clusterSize >= 1) {
- json cja = json::array();
- for(unsigned int i=0;i<cs.clusterSize;++i) {
- json cjm;
- cjm["id"] = (int)cs.members[i].id;
- cjm["msSinceLastHeartbeat"] = cs.members[i].msSinceLastHeartbeat;
- cjm["alive"] = (bool)(cs.members[i].alive != 0);
- cjm["x"] = cs.members[i].x;
- cjm["y"] = cs.members[i].y;
- cjm["z"] = cs.members[i].z;
- cjm["load"] = cs.members[i].load;
- cjm["peers"] = cs.members[i].peers;
- cja.push_back(cjm);
- }
- cj["members"] = cja;
- cj["myId"] = (int)cs.myId;
- cj["clusterSize"] = cs.clusterSize;
- }
- res["cluster"] = cj;
-#else
- res["cluster"] = json();
-#endif
-
scode = 200;
} else if (ps[0] == "moon") {
std::vector<World> moons(_node->moons());
@@ -1301,7 +1284,7 @@ public:
if ((scode != 200)&&(seed != 0)) {
char tmp[64];
- Utils::snprintf(tmp,sizeof(tmp),"%.16llx",id);
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",id);
res["id"] = tmp;
res["roots"] = json::array();
res["timestamp"] = 0;
@@ -1439,7 +1422,7 @@ public:
json &tryAddrs = v.value()["try"];
if (tryAddrs.is_array()) {
for(unsigned long i=0;i<tryAddrs.size();++i) {
- const InetAddress ip(OSUtils::jsonString(tryAddrs[i],""));
+ const InetAddress ip(OSUtils::jsonString(tryAddrs[i],"").c_str());
if (ip.ss_family == AF_INET)
v4h.push_back(ip);
else if (ip.ss_family == AF_INET6)
@@ -1449,7 +1432,7 @@ public:
json &blAddrs = v.value()["blacklist"];
if (blAddrs.is_array()) {
for(unsigned long i=0;i<blAddrs.size();++i) {
- const InetAddress ip(OSUtils::jsonString(tryAddrs[i],""));
+ const InetAddress ip(OSUtils::jsonString(blAddrs[i],"").c_str());
if (ip.ss_family == AF_INET)
v4b.push_back(ip);
else if (ip.ss_family == AF_INET6)
@@ -1471,7 +1454,7 @@ public:
json &physical = lc["physical"];
if (physical.is_object()) {
for(json::iterator phy(physical.begin());phy!=physical.end();++phy) {
- const InetAddress net(OSUtils::jsonString(phy.key(),""));
+ const InetAddress net(OSUtils::jsonString(phy.key(),"").c_str());
if ((net)&&(net.netmaskBits() > 0)) {
if (phy.value().is_object()) {
if (OSUtils::jsonBool(phy.value()["blacklist"],false)) {
@@ -1491,8 +1474,10 @@ public:
json &settings = lc["settings"];
_primaryPort = (unsigned int)OSUtils::jsonInt(settings["primaryPort"],(uint64_t)_primaryPort) & 0xffff;
+ _allowTcpFallbackRelay = OSUtils::jsonBool(settings["allowTcpFallbackRelay"],true);
_portMappingEnabled = OSUtils::jsonBool(settings["portMappingEnabled"],true);
+#ifndef ZT_SDK
const std::string up(OSUtils::jsonString(settings["softwareUpdate"],ZT_SOFTWARE_UPDATE_DEFAULT));
const bool udist = OSUtils::jsonBool(settings["softwareUpdateDist"],false);
if (((up == "apply")||(up == "download"))||(udist)) {
@@ -1506,6 +1491,7 @@ public:
_updater = (SoftwareUpdater *)0;
_updateAutoApply = false;
}
+#endif
json &ignoreIfs = settings["interfacePrefixBlacklist"];
if (ignoreIfs.is_array()) {
@@ -1519,32 +1505,11 @@ public:
json &amf = settings["allowManagementFrom"];
if (amf.is_array()) {
for(unsigned long i=0;i<amf.size();++i) {
- const InetAddress nw(OSUtils::jsonString(amf[i],""));
+ const InetAddress nw(OSUtils::jsonString(amf[i],"").c_str());
if (nw)
_allowManagementFrom.push_back(nw);
}
}
-
- json &controllerDbHttpHost = settings["controllerDbHttpHost"];
- json &controllerDbHttpPort = settings["controllerDbHttpPort"];
- json &controllerDbHttpPath = settings["controllerDbHttpPath"];
- if ((controllerDbHttpHost.is_string())&&(controllerDbHttpPort.is_number())) {
- _controllerDbPath = "http://";
- std::string h = controllerDbHttpHost;
- _controllerDbPath.append(h);
- char dbp[128];
- Utils::snprintf(dbp,sizeof(dbp),"%d",(int)controllerDbHttpPort);
- _controllerDbPath.push_back(':');
- _controllerDbPath.append(dbp);
- if (controllerDbHttpPath.is_string()) {
- std::string p = controllerDbHttpPath;
- if ((p.length() == 0)||(p[0] != '/'))
- _controllerDbPath.push_back('/');
- _controllerDbPath.append(p);
- } else {
- _controllerDbPath.push_back('/');
- }
- }
}
// Checks if a managed IP or route target is allowed
@@ -1592,6 +1557,8 @@ public:
// Apply or update managed IPs for a configured network (be sure n.tap exists)
void syncManagedStuff(NetworkState &n,bool syncIps,bool syncRoutes)
{
+ char ipbuf[64];
+
// assumes _nets_m is locked
if (syncIps) {
std::vector<InetAddress> newManagedIps;
@@ -1607,7 +1574,7 @@ public:
for(std::vector<InetAddress>::iterator ip(n.managedIps.begin());ip!=n.managedIps.end();++ip) {
if (std::find(newManagedIps.begin(),newManagedIps.end(),*ip) == newManagedIps.end()) {
if (!n.tap->removeIp(*ip))
- fprintf(stderr,"ERROR: unable to remove ip address %s" ZT_EOL_S, ip->toString().c_str());
+ fprintf(stderr,"ERROR: unable to remove ip address %s" ZT_EOL_S, ip->toString(ipbuf));
}
}
#ifdef __SYNOLOGY__
@@ -1617,7 +1584,7 @@ public:
for(std::vector<InetAddress>::iterator ip(newManagedIps.begin());ip!=newManagedIps.end();++ip) {
if (std::find(n.managedIps.begin(),n.managedIps.end(),*ip) == n.managedIps.end()) {
if (!n.tap->addIp(*ip))
- fprintf(stderr,"ERROR: unable to add ip address %s" ZT_EOL_S, ip->toString().c_str());
+ fprintf(stderr,"ERROR: unable to add ip address %s" ZT_EOL_S, ip->toString(ipbuf));
}
}
#endif
@@ -1626,8 +1593,8 @@ public:
if (syncRoutes) {
char tapdev[64];
-#ifdef __WINDOWS__
- Utils::snprintf(tapdev,sizeof(tapdev),"%.16llx",(unsigned long long)n.tap->luid().Value);
+#if defined(__WINDOWS__) && !defined(ZT_SDK)
+ OSUtils::ztsnprintf(tapdev,sizeof(tapdev),"%.16llx",(unsigned long long)n.tap->luid().Value);
#else
Utils::scopy(tapdev,sizeof(tapdev),n.tap->deviceName().c_str());
#endif
@@ -1665,12 +1632,15 @@ public:
bool haveRoute = false;
// Ignore routes implied by local managed IPs since adding the IP adds the route
+ // Commented out to fix ticket #600 (disappearing routes on macOS). Remove this block when we're sure there's no side effects
+ /*
for(std::vector<InetAddress>::iterator ip(n.managedIps.begin());ip!=n.managedIps.end();++ip) {
if ((target->netmaskBits() == ip->netmaskBits())&&(target->containsAddress(*ip))) {
haveRoute = true;
break;
}
}
+ */
if (haveRoute)
continue;
@@ -1699,33 +1669,19 @@ public:
inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len)
{
-#ifdef ZT_ENABLE_CLUSTER
- if (sock == _clusterMessageSocket) {
- _lastDirectReceiveFromGlobal = OSUtils::now();
- _node->clusterHandleIncomingMessage(data,len);
- return;
- }
-#endif
-
-#ifdef ZT_BREAK_UDP
- if (OSUtils::fileExists("/tmp/ZT_BREAK_UDP"))
- return;
-#endif
-
if ((len >= 16)&&(reinterpret_cast<const InetAddress *>(from)->ipScope() == InetAddress::IP_SCOPE_GLOBAL))
_lastDirectReceiveFromGlobal = OSUtils::now();
-
const ZT_ResultCode rc = _node->processWirePacket(
(void *)0,
OSUtils::now(),
- reinterpret_cast<const struct sockaddr_storage *>(localAddr),
- (const struct sockaddr_storage *)from, // Phy<> uses sockaddr_storage, so it'll always be that big
+ reinterpret_cast<int64_t>(sock),
+ reinterpret_cast<const struct sockaddr_storage *>(from), // Phy<> uses sockaddr_storage, so it'll always be that big
data,
len,
&_nextBackgroundTaskDeadline);
if (ZT_ResultCode_isFatal(rc)) {
char tmp[256];
- Utils::snprintf(tmp,sizeof(tmp),"fatal error code from processWirePacket: %d",(int)rc);
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"fatal error code from processWirePacket: %d",(int)rc);
Mutex::Lock _l(_termReason_m);
_termReason = ONE_UNRECOVERABLE_ERROR;
_fatalErrorMessage = tmp;
@@ -1735,38 +1691,26 @@ public:
inline void phyOnTcpConnect(PhySocket *sock,void **uptr,bool success)
{
- if (!success)
+ if (!success) {
+ phyOnTcpClose(sock,uptr);
return;
+ }
- // Outgoing TCP connections are always TCP fallback tunnel connections.
-
- TcpConnection *tc = new TcpConnection();
- _tcpConnections.insert(tc);
-
- tc->type = TcpConnection::TCP_TUNNEL_OUTGOING;
- tc->shouldKeepAlive = true;
- tc->parent = this;
+ TcpConnection *const tc = reinterpret_cast<TcpConnection *>(*uptr);
+ if (!tc) { // sanity check
+ _phy.close(sock,true);
+ return;
+ }
tc->sock = sock;
- // from and parser are not used
- tc->messageSize = 0; // unused
- tc->lastActivity = OSUtils::now();
- // HTTP stuff is not used
- tc->writeBuf = "";
- *uptr = (void *)tc;
-
- // Send "hello" message
- tc->writeBuf.push_back((char)0x17);
- tc->writeBuf.push_back((char)0x03);
- tc->writeBuf.push_back((char)0x03); // fake TLS 1.2 header
- tc->writeBuf.push_back((char)0x00);
- tc->writeBuf.push_back((char)0x04); // mlen == 4
- tc->writeBuf.push_back((char)ZEROTIER_ONE_VERSION_MAJOR);
- tc->writeBuf.push_back((char)ZEROTIER_ONE_VERSION_MINOR);
- tc->writeBuf.push_back((char)((ZEROTIER_ONE_VERSION_REVISION >> 8) & 0xff));
- tc->writeBuf.push_back((char)(ZEROTIER_ONE_VERSION_REVISION & 0xff));
- _phy.setNotifyWritable(sock,true);
-
- _tcpFallbackTunnel = tc;
+
+ if (tc->type == TcpConnection::TCP_TUNNEL_OUTGOING) {
+ if (_tcpFallbackTunnel)
+ _phy.close(_tcpFallbackTunnel->sock);
+ _tcpFallbackTunnel = tc;
+ _phy.streamSend(sock,ZT_TCP_TUNNEL_HELLO,sizeof(ZT_TCP_TUNNEL_HELLO));
+ } else {
+ _phy.close(sock,true);
+ }
}
inline void phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from)
@@ -1776,149 +1720,198 @@ public:
return;
} else {
TcpConnection *tc = new TcpConnection();
- _tcpConnections.insert(tc);
- tc->type = TcpConnection::TCP_HTTP_INCOMING;
- tc->shouldKeepAlive = true;
+ {
+ Mutex::Lock _l(_tcpConnections_m);
+ _tcpConnections.push_back(tc);
+ }
+
+ tc->type = TcpConnection::TCP_UNCATEGORIZED_INCOMING;
tc->parent = this;
tc->sock = sockN;
- tc->from = from;
+ tc->remoteAddr = from;
+ tc->lastReceive = OSUtils::now();
http_parser_init(&(tc->parser),HTTP_REQUEST);
tc->parser.data = (void *)tc;
tc->messageSize = 0;
- tc->lastActivity = OSUtils::now();
- tc->currentHeaderField = "";
- tc->currentHeaderValue = "";
- tc->url = "";
- tc->status = "";
- tc->headers.clear();
- tc->body = "";
- tc->writeBuf = "";
+
*uptrN = (void *)tc;
}
}
- inline void phyOnTcpClose(PhySocket *sock,void **uptr)
+ void phyOnTcpClose(PhySocket *sock,void **uptr)
{
TcpConnection *tc = (TcpConnection *)*uptr;
if (tc) {
- if (tc == _tcpFallbackTunnel)
+ if (tc == _tcpFallbackTunnel) {
_tcpFallbackTunnel = (TcpConnection *)0;
- _tcpConnections.erase(tc);
+ }
+ {
+ Mutex::Lock _l(_tcpConnections_m);
+ _tcpConnections.erase(std::remove(_tcpConnections.begin(),_tcpConnections.end(),tc),_tcpConnections.end());
+ }
delete tc;
}
}
- inline void phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len)
+ void phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len)
{
- TcpConnection *tc = reinterpret_cast<TcpConnection *>(*uptr);
- switch(tc->type) {
+ try {
+ if (!len) return; // sanity check, should never happen
+ TcpConnection *tc = reinterpret_cast<TcpConnection *>(*uptr);
+ tc->lastReceive = OSUtils::now();
+ switch(tc->type) {
+
+ case TcpConnection::TCP_UNCATEGORIZED_INCOMING:
+ switch(reinterpret_cast<uint8_t *>(data)[0]) {
+ // HTTP: GET, PUT, POST, HEAD, DELETE
+ case 'G':
+ case 'P':
+ case 'D':
+ case 'H': {
+ // This is only allowed from IPs permitted to access the management
+ // backplane, which is just 127.0.0.1/::1 unless otherwise configured.
+ bool allow;
+ {
+ Mutex::Lock _l(_localConfig_m);
+ if (_allowManagementFrom.size() == 0) {
+ allow = (tc->remoteAddr.ipScope() == InetAddress::IP_SCOPE_LOOPBACK);
+ } else {
+ allow = false;
+ for(std::vector<InetAddress>::const_iterator i(_allowManagementFrom.begin());i!=_allowManagementFrom.end();++i) {
+ if (i->containsAddress(tc->remoteAddr)) {
+ allow = true;
+ break;
+ }
+ }
+ }
+ }
+ if (allow) {
+ tc->type = TcpConnection::TCP_HTTP_INCOMING;
+ phyOnTcpData(sock,uptr,data,len);
+ } else {
+ _phy.close(sock);
+ }
+ } break;
- case TcpConnection::TCP_HTTP_INCOMING:
- case TcpConnection::TCP_HTTP_OUTGOING:
- http_parser_execute(&(tc->parser),&HTTP_PARSER_SETTINGS,(const char *)data,len);
- if ((tc->parser.upgrade)||(tc->parser.http_errno != HPE_OK)) {
- _phy.close(sock);
+ // Drop unknown protocols
+ default:
+ _phy.close(sock);
+ break;
+ }
return;
- }
- break;
- case TcpConnection::TCP_TUNNEL_OUTGOING:
- tc->body.append((const char *)data,len);
- while (tc->body.length() >= 5) {
- const char *data = tc->body.data();
- const unsigned long mlen = ( ((((unsigned long)data[3]) & 0xff) << 8) | (((unsigned long)data[4]) & 0xff) );
- if (tc->body.length() >= (mlen + 5)) {
- InetAddress from;
+ case TcpConnection::TCP_HTTP_INCOMING:
+ case TcpConnection::TCP_HTTP_OUTGOING:
+ http_parser_execute(&(tc->parser),&HTTP_PARSER_SETTINGS,(const char *)data,len);
+ if ((tc->parser.upgrade)||(tc->parser.http_errno != HPE_OK))
+ _phy.close(sock);
+ return;
+
+ case TcpConnection::TCP_TUNNEL_OUTGOING:
+ tc->readq.append((const char *)data,len);
+ while (tc->readq.length() >= 5) {
+ const char *data = tc->readq.data();
+ const unsigned long mlen = ( ((((unsigned long)data[3]) & 0xff) << 8) | (((unsigned long)data[4]) & 0xff) );
+ if (tc->readq.length() >= (mlen + 5)) {
+ InetAddress from;
unsigned long plen = mlen; // payload length, modified if there's an IP header
- data += 5; // skip forward past pseudo-TLS junk and mlen
- if (plen == 4) {
- // Hello message, which isn't sent by proxy and would be ignored by client
- } else if (plen) {
- // Messages should contain IPv4 or IPv6 source IP address data
- switch(data[0]) {
- case 4: // IPv4
- if (plen >= 7) {
- from.set((const void *)(data + 1),4,((((unsigned int)data[5]) & 0xff) << 8) | (((unsigned int)data[6]) & 0xff));
- data += 7; // type + 4 byte IP + 2 byte port
- plen -= 7;
- } else {
+ data += 5; // skip forward past pseudo-TLS junk and mlen
+ if (plen == 4) {
+ // Hello message, which isn't sent by proxy and would be ignored by client
+ } else if (plen) {
+ // Messages should contain IPv4 or IPv6 source IP address data
+ switch(data[0]) {
+ case 4: // IPv4
+ if (plen >= 7) {
+ from.set((const void *)(data + 1),4,((((unsigned int)data[5]) & 0xff) << 8) | (((unsigned int)data[6]) & 0xff));
+ data += 7; // type + 4 byte IP + 2 byte port
+ plen -= 7;
+ } else {
+ _phy.close(sock);
+ return;
+ }
+ break;
+ case 6: // IPv6
+ if (plen >= 19) {
+ from.set((const void *)(data + 1),16,((((unsigned int)data[17]) & 0xff) << 8) | (((unsigned int)data[18]) & 0xff));
+ data += 19; // type + 16 byte IP + 2 byte port
+ plen -= 19;
+ } else {
+ _phy.close(sock);
+ return;
+ }
+ break;
+ case 0: // none/omitted
+ ++data;
+ --plen;
+ break;
+ default: // invalid address type
_phy.close(sock);
return;
- }
- break;
- case 6: // IPv6
- if (plen >= 19) {
- from.set((const void *)(data + 1),16,((((unsigned int)data[17]) & 0xff) << 8) | (((unsigned int)data[18]) & 0xff));
- data += 19; // type + 16 byte IP + 2 byte port
- plen -= 19;
- } else {
+ }
+
+ if (from) {
+ InetAddress fakeTcpLocalInterfaceAddress((uint32_t)0xffffffff,0xffff);
+ const ZT_ResultCode rc = _node->processWirePacket(
+ (void *)0,
+ OSUtils::now(),
+ -1,
+ reinterpret_cast<struct sockaddr_storage *>(&from),
+ data,
+ plen,
+ &_nextBackgroundTaskDeadline);
+ if (ZT_ResultCode_isFatal(rc)) {
+ char tmp[256];
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"fatal error code from processWirePacket: %d",(int)rc);
+ Mutex::Lock _l(_termReason_m);
+ _termReason = ONE_UNRECOVERABLE_ERROR;
+ _fatalErrorMessage = tmp;
+ this->terminate();
_phy.close(sock);
return;
}
- break;
- case 0: // none/omitted
- ++data;
- --plen;
- break;
- default: // invalid address type
- _phy.close(sock);
- return;
- }
-
- if (from) {
- InetAddress fakeTcpLocalInterfaceAddress((uint32_t)0xffffffff,0xffff);
- const ZT_ResultCode rc = _node->processWirePacket(
- (void *)0,
- OSUtils::now(),
- reinterpret_cast<struct sockaddr_storage *>(&fakeTcpLocalInterfaceAddress),
- reinterpret_cast<struct sockaddr_storage *>(&from),
- data,
- plen,
- &_nextBackgroundTaskDeadline);
- if (ZT_ResultCode_isFatal(rc)) {
- char tmp[256];
- Utils::snprintf(tmp,sizeof(tmp),"fatal error code from processWirePacket: %d",(int)rc);
- Mutex::Lock _l(_termReason_m);
- _termReason = ONE_UNRECOVERABLE_ERROR;
- _fatalErrorMessage = tmp;
- this->terminate();
- _phy.close(sock);
- return;
}
}
- }
- if (tc->body.length() > (mlen + 5))
- tc->body = tc->body.substr(mlen + 5);
- else tc->body = "";
- } else break;
- }
- break;
+ if (tc->readq.length() > (mlen + 5))
+ tc->readq.erase(tc->readq.begin(),tc->readq.begin() + (mlen + 5));
+ else tc->readq.clear();
+ } else break;
+ }
+ return;
+ }
+ } catch ( ... ) {
+ _phy.close(sock);
}
}
inline void phyOnTcpWritable(PhySocket *sock,void **uptr)
{
TcpConnection *tc = reinterpret_cast<TcpConnection *>(*uptr);
- Mutex::Lock _l(tc->writeBuf_m);
- if (tc->writeBuf.length() > 0) {
- long sent = (long)_phy.streamSend(sock,tc->writeBuf.data(),(unsigned long)tc->writeBuf.length(),true);
- if (sent > 0) {
- tc->lastActivity = OSUtils::now();
- if ((unsigned long)sent >= (unsigned long)tc->writeBuf.length()) {
- tc->writeBuf = "";
- _phy.setNotifyWritable(sock,false);
- if (!tc->shouldKeepAlive)
- _phy.close(sock); // will call close handler to delete from _tcpConnections
- } else {
- tc->writeBuf = tc->writeBuf.substr(sent);
+ bool closeit = false;
+ {
+ Mutex::Lock _l(tc->writeq_m);
+ if (tc->writeq.length() > 0) {
+ long sent = (long)_phy.streamSend(sock,tc->writeq.data(),(unsigned long)tc->writeq.length(),true);
+ if (sent > 0) {
+ if ((unsigned long)sent >= (unsigned long)tc->writeq.length()) {
+ tc->writeq.clear();
+ _phy.setNotifyWritable(sock,false);
+
+ if (tc->type == TcpConnection::TCP_HTTP_INCOMING)
+ closeit = true; // HTTP keep alive not supported
+ } else {
+ tc->writeq.erase(tc->writeq.begin(),tc->writeq.begin() + sent);
+ }
}
+ } else {
+ _phy.setNotifyWritable(sock,false);
}
- } else {
- _phy.setNotifyWritable(sock,false);
}
+ if (closeit)
+ _phy.close(sock);
}
inline void phyOnFileDescriptorActivity(PhySocket *sock,void **uptr,bool readable,bool writable) {}
@@ -1938,7 +1931,7 @@ public:
if (!n.tap) {
try {
char friendlyName[128];
- Utils::snprintf(friendlyName,sizeof(friendlyName),"ZeroTier One [%.16llx]",nwid);
+ OSUtils::ztsnprintf(friendlyName,sizeof(friendlyName),"ZeroTier One [%.16llx]",nwid);
n.tap = new EthernetTap(
_homePath.c_str(),
@@ -1952,7 +1945,7 @@ public:
*nuptr = (void *)&n;
char nlcpath[256];
- Utils::snprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_homePath.c_str(),nwid);
+ OSUtils::ztsnprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_homePath.c_str(),nwid);
std::string nlcbuf;
if (OSUtils::readFile(nlcpath,nlcbuf)) {
Dictionary<4096> nc;
@@ -1973,7 +1966,7 @@ public:
while (true) {
size_t nextPos = addresses.find(',', pos);
std::string address = addresses.substr(pos, (nextPos == std::string::npos ? addresses.size() : nextPos) - pos);
- n.settings.allowManagedWhitelist.push_back(InetAddress(address));
+ n.settings.allowManagedWhitelist.push_back(InetAddress(address.c_str()));
if (nextPos == std::string::npos) break;
pos = nextPos + 1;
}
@@ -2003,19 +1996,20 @@ public:
// After setting up tap, fall through to CONFIG_UPDATE since we also want to do this...
case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE:
- memcpy(&(n.config),nwc,sizeof(ZT_VirtualNetworkConfig));
+ ZT_FAST_MEMCPY(&(n.config),nwc,sizeof(ZT_VirtualNetworkConfig));
if (n.tap) { // sanity check
-#ifdef __WINDOWS__
- // wait for up to 5 seconds for the WindowsEthernetTap to actually be initialized
- //
- // without WindowsEthernetTap::isInitialized() returning true, the won't actually
- // be online yet and setting managed routes on it will fail.
- const int MAX_SLEEP_COUNT = 500;
- for (int i = 0; !n.tap->isInitialized() && i < MAX_SLEEP_COUNT; i++) {
- Sleep(10);
- }
+#if defined(__WINDOWS__) && !defined(ZT_SDK)
+ // wait for up to 5 seconds for the WindowsEthernetTap to actually be initialized
+ //
+ // without WindowsEthernetTap::isInitialized() returning true, the won't actually
+ // be online yet and setting managed routes on it will fail.
+ const int MAX_SLEEP_COUNT = 500;
+ for (int i = 0; !n.tap->isInitialized() && i < MAX_SLEEP_COUNT; i++) {
+ Sleep(10);
+ }
#endif
syncManagedStuff(n,true,true);
+ n.tap->setMtu(nwc->mtu);
} else {
_nets.erase(nwid);
return -999; // tap init failed
@@ -2025,19 +2019,19 @@ public:
case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN:
case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY:
if (n.tap) { // sanity check
-#ifdef __WINDOWS__
+#if defined(__WINDOWS__) && !defined(ZT_SDK)
std::string winInstanceId(n.tap->instanceId());
#endif
*nuptr = (void *)0;
delete n.tap;
_nets.erase(nwid);
-#ifdef __WINDOWS__
+#if defined(__WINDOWS__) && !defined(ZT_SDK)
if ((op == ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY)&&(winInstanceId.length() > 0))
WindowsEthernetTap::deletePersistentTapDevice(winInstanceId.c_str());
#endif
if (op == ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY) {
char nlcpath[256];
- Utils::snprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_homePath.c_str(),nwid);
+ OSUtils::ztsnprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_homePath.c_str(),nwid);
OSUtils::rm(nlcpath);
}
} else {
@@ -2073,133 +2067,192 @@ public:
}
} break;
+ case ZT_EVENT_REMOTE_TRACE: {
+ const ZT_RemoteTrace *rt = reinterpret_cast<const ZT_RemoteTrace *>(metaData);
+ if ((rt)&&(rt->len > 0)&&(rt->len <= ZT_MAX_REMOTE_TRACE_SIZE)&&(rt->data))
+ _controller->handleRemoteTrace(*rt);
+ }
+
default:
break;
}
}
- inline long nodeDataStoreGetFunction(const char *name,void *buf,unsigned long bufSize,unsigned long readIndex,unsigned long *totalSize)
+ inline void nodeStatePutFunction(enum ZT_StateObjectType type,const uint64_t id[2],const void *data,int len)
{
- std::string p(_dataStorePrepPath(name));
- if (!p.length())
- return -2;
-
- FILE *f = fopen(p.c_str(),"rb");
- if (!f)
- return -1;
- if (fseek(f,0,SEEK_END) != 0) {
- fclose(f);
- return -2;
- }
- long ts = ftell(f);
- if (ts < 0) {
- fclose(f);
- return -2;
+ char p[1024];
+ FILE *f;
+ bool secure = false;
+ char dirname[1024];
+ dirname[0] = 0;
+
+ switch(type) {
+ case ZT_STATE_OBJECT_IDENTITY_PUBLIC:
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.public",_homePath.c_str());
+ break;
+ case ZT_STATE_OBJECT_IDENTITY_SECRET:
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.secret",_homePath.c_str());
+ secure = true;
+ break;
+ case ZT_STATE_OBJECT_PLANET:
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "planet",_homePath.c_str());
+ break;
+ case ZT_STATE_OBJECT_MOON:
+ OSUtils::ztsnprintf(dirname,sizeof(dirname),"%s" ZT_PATH_SEPARATOR_S "moons.d",_homePath.c_str());
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.16llx.moon",dirname,(unsigned long long)id[0]);
+ break;
+ case ZT_STATE_OBJECT_NETWORK_CONFIG:
+ OSUtils::ztsnprintf(dirname,sizeof(dirname),"%s" ZT_PATH_SEPARATOR_S "networks.d",_homePath.c_str());
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.16llx.conf",dirname,(unsigned long long)id[0]);
+ secure = true;
+ break;
+ case ZT_STATE_OBJECT_PEER:
+ OSUtils::ztsnprintf(dirname,sizeof(dirname),"%s" ZT_PATH_SEPARATOR_S "peers.d",_homePath.c_str());
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.10llx.peer",dirname,(unsigned long long)id[0]);
+ break;
+ default:
+ return;
}
- *totalSize = (unsigned long)ts;
- if (fseek(f,(long)readIndex,SEEK_SET) != 0) {
- fclose(f);
- return -2;
+
+ if (len >= 0) {
+ // Check to see if we've already written this first. This reduces
+ // redundant writes and I/O overhead on most platforms and has
+ // little effect on others.
+ f = fopen(p,"rb");
+ if (f) {
+ char buf[65535];
+ long l = (long)fread(buf,1,sizeof(buf),f);
+ fclose(f);
+ if ((l == (long)len)&&(memcmp(data,buf,l) == 0))
+ return;
+ }
+
+ f = fopen(p,"wb");
+ if ((!f)&&(dirname[0])) { // create subdirectory if it does not exist
+ OSUtils::mkdir(dirname);
+ f = fopen(p,"wb");
+ }
+ if (f) {
+ if (fwrite(data,len,1,f) != 1)
+ fprintf(stderr,"WARNING: unable to write to file: %s (I/O error)" ZT_EOL_S,p);
+ fclose(f);
+ if (secure)
+ OSUtils::lockDownFile(p,false);
+ } else {
+ fprintf(stderr,"WARNING: unable to write to file: %s (unable to open)" ZT_EOL_S,p);
+ }
+ } else {
+ OSUtils::rm(p);
}
- long n = (long)fread(buf,1,bufSize,f);
- fclose(f);
- return n;
}
- inline int nodeDataStorePutFunction(const char *name,const void *data,unsigned long len,int secure)
+ inline int nodeStateGetFunction(enum ZT_StateObjectType type,const uint64_t id[2],void *data,unsigned int maxlen)
{
- std::string p(_dataStorePrepPath(name));
- if (!p.length())
- return -2;
-
- if (!data) {
- OSUtils::rm(p.c_str());
- return 0;
+ char p[4096];
+ switch(type) {
+ case ZT_STATE_OBJECT_IDENTITY_PUBLIC:
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.public",_homePath.c_str());
+ break;
+ case ZT_STATE_OBJECT_IDENTITY_SECRET:
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.secret",_homePath.c_str());
+ break;
+ case ZT_STATE_OBJECT_PLANET:
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "planet",_homePath.c_str());
+ break;
+ case ZT_STATE_OBJECT_MOON:
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "moons.d" ZT_PATH_SEPARATOR_S "%.16llx.moon",_homePath.c_str(),(unsigned long long)id);
+ break;
+ case ZT_STATE_OBJECT_NETWORK_CONFIG:
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.conf",_homePath.c_str(),(unsigned long long)id);
+ break;
+ case ZT_STATE_OBJECT_PEER:
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "peers.d" ZT_PATH_SEPARATOR_S "%.10llx.peer",_homePath.c_str(),(unsigned long long)id[0]);
+ break;
+ default:
+ return -1;
}
-
- FILE *f = fopen(p.c_str(),"wb");
- if (!f)
- return -1;
- if (fwrite(data,len,1,f) == 1) {
- fclose(f);
- if (secure)
- OSUtils::lockDownFile(p.c_str(),false);
- return 0;
- } else {
+ FILE *f = fopen(p,"rb");
+ if (f) {
+ int n = (int)fread(data,1,maxlen,f);
fclose(f);
- OSUtils::rm(p.c_str());
- return -1;
+ if (n >= 0)
+ return n;
}
+ return -1;
}
- inline int nodeWirePacketSendFunction(const struct sockaddr_storage *localAddr,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl)
+ inline int nodeWirePacketSendFunction(const int64_t localSocket,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl)
{
- unsigned int fromBindingNo = 0;
-
- if (addr->ss_family == AF_INET) {
- if (reinterpret_cast<const struct sockaddr_in *>(localAddr)->sin_port == 0) {
- // If sender is sending from wildcard (null address), choose the secondary backup
- // port 1/4 of the time. (but only for IPv4)
- fromBindingNo = (++_udpPortPickerCounter & 0x4) >> 2;
- if (!_ports[fromBindingNo])
- fromBindingNo = 0;
- } else {
- const uint16_t lp = reinterpret_cast<const struct sockaddr_in *>(localAddr)->sin_port;
- if (lp == _portsBE[1])
- fromBindingNo = 1;
- else if (lp == _portsBE[2])
- fromBindingNo = 2;
- }
-
#ifdef ZT_TCP_FALLBACK_RELAY
- // TCP fallback tunnel support, currently IPv4 only
- if ((len >= 16)&&(reinterpret_cast<const InetAddress *>(addr)->ipScope() == InetAddress::IP_SCOPE_GLOBAL)) {
- // Engage TCP tunnel fallback if we haven't received anything valid from a global
- // IP address in ZT_TCP_FALLBACK_AFTER milliseconds. If we do start getting
- // valid direct traffic we'll stop using it and close the socket after a while.
- const uint64_t now = OSUtils::now();
- if (((now - _lastDirectReceiveFromGlobal) > ZT_TCP_FALLBACK_AFTER)&&((now - _lastRestart) > ZT_TCP_FALLBACK_AFTER)) {
- if (_tcpFallbackTunnel) {
- Mutex::Lock _l(_tcpFallbackTunnel->writeBuf_m);
- if (!_tcpFallbackTunnel->writeBuf.length())
- _phy.setNotifyWritable(_tcpFallbackTunnel->sock,true);
- unsigned long mlen = len + 7;
- _tcpFallbackTunnel->writeBuf.push_back((char)0x17);
- _tcpFallbackTunnel->writeBuf.push_back((char)0x03);
- _tcpFallbackTunnel->writeBuf.push_back((char)0x03); // fake TLS 1.2 header
- _tcpFallbackTunnel->writeBuf.push_back((char)((mlen >> 8) & 0xff));
- _tcpFallbackTunnel->writeBuf.push_back((char)(mlen & 0xff));
- _tcpFallbackTunnel->writeBuf.push_back((char)4); // IPv4
- _tcpFallbackTunnel->writeBuf.append(reinterpret_cast<const char *>(reinterpret_cast<const void *>(&(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr))),4);
- _tcpFallbackTunnel->writeBuf.append(reinterpret_cast<const char *>(reinterpret_cast<const void *>(&(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_port))),2);
- _tcpFallbackTunnel->writeBuf.append((const char *)data,len);
- } else if (((now - _lastSendToGlobalV4) < ZT_TCP_FALLBACK_AFTER)&&((now - _lastSendToGlobalV4) > (ZT_PING_CHECK_INVERVAL / 2))) {
- bool connected = false;
- const InetAddress addr(ZT_TCP_FALLBACK_RELAY);
- _phy.tcpConnect(reinterpret_cast<const struct sockaddr *>(&addr),connected);
+ if(_allowTcpFallbackRelay) {
+ if (addr->ss_family == AF_INET) {
+ // TCP fallback tunnel support, currently IPv4 only
+ if ((len >= 16)&&(reinterpret_cast<const InetAddress *>(addr)->ipScope() == InetAddress::IP_SCOPE_GLOBAL)) {
+ // Engage TCP tunnel fallback if we haven't received anything valid from a global
+ // IP address in ZT_TCP_FALLBACK_AFTER milliseconds. If we do start getting
+ // valid direct traffic we'll stop using it and close the socket after a while.
+ const int64_t now = OSUtils::now();
+ if (((now - _lastDirectReceiveFromGlobal) > ZT_TCP_FALLBACK_AFTER)&&((now - _lastRestart) > ZT_TCP_FALLBACK_AFTER)) {
+ if (_tcpFallbackTunnel) {
+ bool flushNow = false;
+ {
+ Mutex::Lock _l(_tcpFallbackTunnel->writeq_m);
+ if (_tcpFallbackTunnel->writeq.size() < (1024 * 64)) {
+ if (_tcpFallbackTunnel->writeq.length() == 0) {
+ _phy.setNotifyWritable(_tcpFallbackTunnel->sock,true);
+ flushNow = true;
+ }
+ const unsigned long mlen = len + 7;
+ _tcpFallbackTunnel->writeq.push_back((char)0x17);
+ _tcpFallbackTunnel->writeq.push_back((char)0x03);
+ _tcpFallbackTunnel->writeq.push_back((char)0x03); // fake TLS 1.2 header
+ _tcpFallbackTunnel->writeq.push_back((char)((mlen >> 8) & 0xff));
+ _tcpFallbackTunnel->writeq.push_back((char)(mlen & 0xff));
+ _tcpFallbackTunnel->writeq.push_back((char)4); // IPv4
+ _tcpFallbackTunnel->writeq.append(reinterpret_cast<const char *>(reinterpret_cast<const void *>(&(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr))),4);
+ _tcpFallbackTunnel->writeq.append(reinterpret_cast<const char *>(reinterpret_cast<const void *>(&(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_port))),2);
+ _tcpFallbackTunnel->writeq.append((const char *)data,len);
+ }
+ }
+ if (flushNow) {
+ void *tmpptr = (void *)_tcpFallbackTunnel;
+ phyOnTcpWritable(_tcpFallbackTunnel->sock,&tmpptr);
+ }
+ } else if (((now - _lastSendToGlobalV4) < ZT_TCP_FALLBACK_AFTER)&&((now - _lastSendToGlobalV4) > (ZT_PING_CHECK_INVERVAL / 2))) {
+ const InetAddress addr(ZT_TCP_FALLBACK_RELAY);
+ TcpConnection *tc = new TcpConnection();
+ {
+ Mutex::Lock _l(_tcpConnections_m);
+ _tcpConnections.push_back(tc);
+ }
+ tc->type = TcpConnection::TCP_TUNNEL_OUTGOING;
+ tc->remoteAddr = addr;
+ tc->lastReceive = OSUtils::now();
+ tc->parent = this;
+ tc->sock = (PhySocket *)0; // set in connect handler
+ tc->messageSize = 0;
+ bool connected = false;
+ _phy.tcpConnect(reinterpret_cast<const struct sockaddr *>(&addr),connected,(void *)tc,true);
+ }
}
+ _lastSendToGlobalV4 = now;
}
- _lastSendToGlobalV4 = now;
}
-#endif // ZT_TCP_FALLBACK_RELAY
- } else if (addr->ss_family == AF_INET6) {
- if (reinterpret_cast<const struct sockaddr_in6 *>(localAddr)->sin6_port != 0) {
- const uint16_t lp = reinterpret_cast<const struct sockaddr_in6 *>(localAddr)->sin6_port;
- if (lp == _portsBE[1])
- fromBindingNo = 1;
- else if (lp == _portsBE[2])
- fromBindingNo = 2;
- }
- } else {
- return -1;
}
+#endif // ZT_TCP_FALLBACK_RELAY
-#ifdef ZT_BREAK_UDP
- if (OSUtils::fileExists("/tmp/ZT_BREAK_UDP"))
- return 0; // silently break UDP
-#endif
+ // Even when relaying we still send via UDP. This way if UDP starts
+ // working we can instantly "fail forward" to it and stop using TCP
+ // proxy fallback, which is slow.
- return (_bindings[fromBindingNo].udpSend(_phy,*(reinterpret_cast<const InetAddress *>(localAddr)),*(reinterpret_cast<const InetAddress *>(addr)),data,len,ttl)) ? 0 : -1;
+ if ((localSocket != -1)&&(localSocket != 0)&&(_binder.isUdpSocketValid((PhySocket *)((uintptr_t)localSocket)))) {
+ if ((ttl)&&(addr->ss_family == AF_INET)) _phy.setIp4UdpTtl((PhySocket *)((uintptr_t)localSocket),ttl);
+ const bool r = _phy.udpSend((PhySocket *)((uintptr_t)localSocket),(const struct sockaddr *)addr,data,len);
+ if ((ttl)&&(addr->ss_family == AF_INET)) _phy.setIp4UdpTtl((PhySocket *)((uintptr_t)localSocket),255);
+ return ((r) ? 0 : -1);
+ } else {
+ return ((_binder.udpSendAll(_phy,addr,data,len,ttl)) ? 0 : -1);
+ }
}
inline void nodeVirtualNetworkFrameFunction(uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
@@ -2210,7 +2263,7 @@ public:
n->tap->put(MAC(sourceMac),MAC(destMac),etherType,data,len);
}
- inline int nodePathCheckFunction(uint64_t ztaddr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr)
+ inline int nodePathCheckFunction(uint64_t ztaddr,const int64_t localSocket,const struct sockaddr_storage *remoteAddr)
{
// Make sure we're not trying to do ZeroTier-over-ZeroTier
{
@@ -2252,12 +2305,13 @@ public:
return 0;
}
}
+ }
+ if (gbl) {
for(std::vector<InetAddress>::const_iterator a(gbl->begin());a!=gbl->end();++a) {
if (a->containsAddress(*reinterpret_cast<const InetAddress *>(remoteAddr)))
return 0;
}
}
-
return 1;
}
@@ -2273,7 +2327,7 @@ public:
else return 0;
const std::vector<InetAddress> *l = lh->get(ztaddr);
if ((l)&&(l->size() > 0)) {
- memcpy(result,&((*l)[(unsigned long)_node->prng() % l->size()]),sizeof(struct sockaddr_storage));
+ ZT_FAST_MEMCPY(result,&((*l)[(unsigned long)_node->prng() % l->size()]),sizeof(struct sockaddr_storage));
return 1;
} else return 0;
}
@@ -2285,39 +2339,22 @@ public:
inline void onHttpRequestToServer(TcpConnection *tc)
{
- char tmpn[256];
+ char tmpn[4096];
std::string data;
std::string contentType("text/plain"); // default if not changed in handleRequest()
unsigned int scode = 404;
- bool allow;
- {
- Mutex::Lock _l(_localConfig_m);
- if (_allowManagementFrom.size() == 0) {
- allow = (tc->from.ipScope() == InetAddress::IP_SCOPE_LOOPBACK);
- } else {
- allow = false;
- for(std::vector<InetAddress>::const_iterator i(_allowManagementFrom.begin());i!=_allowManagementFrom.end();++i) {
- if (i->containsAddress(tc->from)) {
- allow = true;
- break;
- }
- }
- }
- }
+ // Note that we check allowed IP ranges when HTTP connections are first detected in
+ // phyOnTcpData(). If we made it here the source IP is okay.
- if (allow) {
- try {
- scode = handleControlPlaneHttpRequest(tc->from,tc->parser.method,tc->url,tc->headers,tc->body,data,contentType);
- } catch (std::exception &exc) {
- fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: %s" ZT_EOL_S,exc.what());
- scode = 500;
- } catch ( ... ) {
- fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: unknown exceptino" ZT_EOL_S);
- scode = 500;
- }
- } else {
- scode = 401;
+ try {
+ scode = handleControlPlaneHttpRequest(tc->remoteAddr,tc->parser.method,tc->url,tc->headers,tc->readq,data,contentType);
+ } catch (std::exception &exc) {
+ fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: %s" ZT_EOL_S,exc.what());
+ scode = 500;
+ } catch ( ... ) {
+ fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: unknown exception" ZT_EOL_S);
+ scode = 500;
}
const char *scodestr;
@@ -2333,19 +2370,16 @@ public:
default: scodestr = "Error"; break;
}
- Utils::snprintf(tmpn,sizeof(tmpn),"HTTP/1.1 %.3u %s\r\nCache-Control: no-cache\r\nPragma: no-cache\r\n",scode,scodestr);
+ OSUtils::ztsnprintf(tmpn,sizeof(tmpn),"HTTP/1.1 %.3u %s\r\nCache-Control: no-cache\r\nPragma: no-cache\r\nContent-Type: %s\r\nContent-Length: %lu\r\nConnection: close\r\n\r\n",
+ scode,
+ scodestr,
+ contentType.c_str(),
+ (unsigned long)data.length());
{
- Mutex::Lock _l(tc->writeBuf_m);
- tc->writeBuf.assign(tmpn);
- tc->writeBuf.append("Content-Type: ");
- tc->writeBuf.append(contentType);
- Utils::snprintf(tmpn,sizeof(tmpn),"\r\nContent-Length: %lu\r\n",(unsigned long)data.length());
- tc->writeBuf.append(tmpn);
- if (!tc->shouldKeepAlive)
- tc->writeBuf.append("Connection: close\r\n");
- tc->writeBuf.append("\r\n");
+ Mutex::Lock _l(tc->writeq_m);
+ tc->writeq = tmpn;
if (tc->parser.method != HTTP_HEAD)
- tc->writeBuf.append(data);
+ tc->writeq.append(data);
}
_phy.setNotifyWritable(tc->sock,true);
@@ -2353,8 +2387,7 @@ public:
inline void onHttpResponseFromClient(TcpConnection *tc)
{
- if (!tc->shouldKeepAlive)
- _phy.close(tc->sock); // will call close handler, which deletes from _tcpConnections
+ _phy.close(tc->sock);
}
bool shouldBindInterface(const char *ifname,const InetAddress &ifaddr)
@@ -2398,23 +2431,6 @@ public:
return true;
}
- std::string _dataStorePrepPath(const char *name) const
- {
- std::string p(_homePath);
- p.push_back(ZT_PATH_SEPARATOR);
- char lastc = (char)0;
- for(const char *n=name;(*n);++n) {
- if ((*n == '.')&&(lastc == '.'))
- return std::string(); // don't allow ../../ stuff as a precaution
- if (*n == '/') {
- OSUtils::mkdir(p.c_str());
- p.push_back(ZT_PATH_SEPARATOR);
- } else p.push_back(*n);
- lastc = *n;
- }
- return p;
- }
-
bool _trialBind(unsigned int port)
{
struct sockaddr_in in4;
@@ -2455,34 +2471,18 @@ static int SnodeVirtualNetworkConfigFunction(ZT_Node *node,void *uptr,void *tptr
{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeVirtualNetworkConfigFunction(nwid,nuptr,op,nwconf); }
static void SnodeEventCallback(ZT_Node *node,void *uptr,void *tptr,enum ZT_Event event,const void *metaData)
{ reinterpret_cast<OneServiceImpl *>(uptr)->nodeEventCallback(event,metaData); }
-static long SnodeDataStoreGetFunction(ZT_Node *node,void *uptr,void *tptr,const char *name,void *buf,unsigned long bufSize,unsigned long readIndex,unsigned long *totalSize)
-{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeDataStoreGetFunction(name,buf,bufSize,readIndex,totalSize); }
-static int SnodeDataStorePutFunction(ZT_Node *node,void *uptr,void *tptr,const char *name,const void *data,unsigned long len,int secure)
-{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeDataStorePutFunction(name,data,len,secure); }
-static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,void *tptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl)
-{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeWirePacketSendFunction(localAddr,addr,data,len,ttl); }
+static void SnodeStatePutFunction(ZT_Node *node,void *uptr,void *tptr,enum ZT_StateObjectType type,const uint64_t id[2],const void *data,int len)
+{ reinterpret_cast<OneServiceImpl *>(uptr)->nodeStatePutFunction(type,id,data,len); }
+static int SnodeStateGetFunction(ZT_Node *node,void *uptr,void *tptr,enum ZT_StateObjectType type,const uint64_t id[2],void *data,unsigned int maxlen)
+{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeStateGetFunction(type,id,data,maxlen); }
+static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,void *tptr,int64_t localSocket,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl)
+{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeWirePacketSendFunction(localSocket,addr,data,len,ttl); }
static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
{ reinterpret_cast<OneServiceImpl *>(uptr)->nodeVirtualNetworkFrameFunction(nwid,nuptr,sourceMac,destMac,etherType,vlanId,data,len); }
-static int SnodePathCheckFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr)
-{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodePathCheckFunction(ztaddr,localAddr,remoteAddr); }
+static int SnodePathCheckFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,int64_t localSocket,const struct sockaddr_storage *remoteAddr)
+{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodePathCheckFunction(ztaddr,localSocket,remoteAddr); }
static int SnodePathLookupFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,int family,struct sockaddr_storage *result)
{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodePathLookupFunction(ztaddr,family,result); }
-
-#ifdef ZT_ENABLE_CLUSTER
-static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len)
-{
- OneServiceImpl *const impl = reinterpret_cast<OneServiceImpl *>(uptr);
- const ClusterDefinition::MemberDefinition &md = (*(impl->_clusterDefinition))[toMemberId];
- if (md.clusterEndpoint)
- impl->_phy.udpSend(impl->_clusterMessageSocket,reinterpret_cast<const struct sockaddr *>(&(md.clusterEndpoint)),data,len);
-}
-static int SclusterGeoIpFunction(void *uptr,const struct sockaddr_storage *addr,int *x,int *y,int *z)
-{
- OneServiceImpl *const impl = reinterpret_cast<OneServiceImpl *>(uptr);
- return (int)(impl->_clusterDefinition->geo().locate(*(reinterpret_cast<const InetAddress *>(addr)),*x,*y,*z));
-}
-#endif
-
static void StapFrameHandler(void *uptr,void *tptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
{ reinterpret_cast<OneServiceImpl *>(uptr)->tapFrameHandler(nwid,from,to,etherType,vlanId,data,len); }
@@ -2492,10 +2492,10 @@ static int ShttpOnMessageBegin(http_parser *parser)
tc->currentHeaderField = "";
tc->currentHeaderValue = "";
tc->messageSize = 0;
- tc->url = "";
- tc->status = "";
+ tc->url.clear();
+ tc->status.clear();
tc->headers.clear();
- tc->body = "";
+ tc->readq.clear();
return 0;
}
static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length)
@@ -2512,16 +2512,7 @@ static int ShttpOnStatus(http_parser *parser,const char *ptr,size_t length)
#else
static int ShttpOnStatus(http_parser *parser)
#endif
-{
- /*
- TcpConnection *tc = reinterpret_cast<TcpConnection *>(parser->data);
- tc->messageSize += (unsigned long)length;
- if (tc->messageSize > ZT_MAX_HTTP_MESSAGE_SIZE)
- return -1;
- tc->status.append(ptr,length);
- */
- return 0;
-}
+{ return 0; }
static int ShttpOnHeaderField(http_parser *parser,const char *ptr,size_t length)
{
TcpConnection *tc = reinterpret_cast<TcpConnection *>(parser->data);
@@ -2559,14 +2550,12 @@ static int ShttpOnBody(http_parser *parser,const char *ptr,size_t length)
tc->messageSize += (unsigned long)length;
if (tc->messageSize > ZT_MAX_HTTP_MESSAGE_SIZE)
return -1;
- tc->body.append(ptr,length);
+ tc->readq.append(ptr,length);
return 0;
}
static int ShttpOnMessageComplete(http_parser *parser)
{
TcpConnection *tc = reinterpret_cast<TcpConnection *>(parser->data);
- tc->shouldKeepAlive = (http_should_keep_alive(parser) != 0);
- tc->lastActivity = OSUtils::now();
if (tc->type == TcpConnection::TCP_HTTP_INCOMING) {
tc->parent->onHttpRequestToServer(tc);
} else {
diff --git a/service/OneService.hpp b/service/OneService.hpp
index 3390f2ac..3b670e4a 100644
--- a/service/OneService.hpp
+++ b/service/OneService.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_ONESERVICE_HPP
@@ -24,6 +32,13 @@
#include "../node/InetAddress.hpp"
+#ifdef ZT_SDK
+#include "../node/Node.hpp"
+// Use the virtual netcon endpoint instead of a tun/tap port driver
+#include "../include/VirtualTap.h"
+namespace ZeroTier { typedef VirtualTap EthernetTap; }
+#endif
+
namespace ZeroTier {
/**
@@ -131,6 +146,15 @@ public:
*/
virtual std::string portDeviceName(uint64_t nwid) const = 0;
+#ifdef ZT_SDK
+ virtual void leave(const uint64_t hp) = 0;
+ virtual void join(const uint64_t hp) = 0;
+ virtual std::string givenHomePath() = 0;
+ virtual Node * getNode() = 0;
+ virtual void removeNets() = 0;
+ virtual std::vector<ZT_VirtualNetworkRoute> *getRoutes(uint64_t nwid) = 0;
+#endif
+
/**
* Terminate background service (can be called from other threads)
*/
diff --git a/service/README.md b/service/README.md
index f5223f2d..da29d3d0 100644
--- a/service/README.md
+++ b/service/README.md
@@ -14,7 +14,8 @@ Settings available in `local.conf` (this is not valid JSON, and JSON does not al
"physical": { /* Settings that apply to physical L2/L3 network paths. */
"NETWORK/bits": { /* Network e.g. 10.0.0.0/24 or fd00::/32 */
"blacklist": true|false, /* If true, blacklist this path for all ZeroTier traffic */
- "trustedPathId": 0|!0 /* If present and nonzero, define this as a trusted path (see below) */
+ "trustedPathId": 0|!0, /* If present and nonzero, define this as a trusted path (see below) */
+ "mtu": 0|!0 /* if present and non-zero, set UDP maximum payload MTU for this path */
} /* ,... additional networks */
},
"virtual": { /* Settings applied to ZeroTier virtual network devices (VL1) */
@@ -30,13 +31,13 @@ Settings available in `local.conf` (this is not valid JSON, and JSON does not al
"softwareUpdateChannel": "release"|"beta", /* Software update channel */
"softwareUpdateDist": true|false, /* If true, distribute software updates (only really useful to ZeroTier, Inc. itself, default is false) */
"interfacePrefixBlacklist": [ "XXX",... ], /* Array of interface name prefixes (e.g. eth for eth#) to blacklist for ZT traffic */
- "allowManagementFrom": "NETWORK/bits"|null /* If non-NULL, allow JSON/HTTP management from this IP network. Default is 127.0.0.1 only. */
+ "allowManagementFrom": "NETWORK/bits"|null, /* If non-NULL, allow JSON/HTTP management from this IP network. Default is 127.0.0.1 only. */
+ "bind": [ "ip",... ] /* If present and non-null, bind to these IPs instead of to each interface (wildcard IP allowed) */
}
}
```
* **trustedPathId**: A trusted path is a physical network over which encryption and authentication are not required. This provides a performance boost but sacrifices all ZeroTier's security features when communicating over this path. Only use this if you know what you are doing and really need the performance! To set up a trusted path, all devices using it *MUST* have the *same trusted path ID* for the same network. Trusted path IDs are arbitrary positive non-zero integers. For example a group of devices on a LAN with IPs in 10.0.0.0/24 could use it as a fast trusted path if they all had the same trusted path ID of "25" defined for that network.
- * **relayPolicy**: Under what circumstances should this device relay traffic for other devices? The default is TRUSTED, meaning that we'll only relay for devices we know to be members of a network we have joined. NEVER is the default on mobile devices (iOS/Android) and tells us to never relay traffic. ALWAYS is usually only set for upstreams and roots, allowing them to act as promiscuous relays for anyone who desires it.
An example `local.conf`:
@@ -59,7 +60,7 @@ An example `local.conf`:
},
"settings": {
"softwareUpdate": "apply",
- "softwraeUpdateChannel": "release"
+ "softwareUpdateChannel": "release"
}
}
```
@@ -165,7 +166,7 @@ Getting /peer returns an array of peer objects for all current peers. See below
| versionRev | integer | Software revision of remote (if known) | no |
| version | string | major.minor.revision | no |
| latency | integer | Latency in milliseconds if known | no |
-| role | string | LEAF, UPSTREAM, or ROOT | no |
+| role | string | LEAF, UPSTREAM, ROOT or PLANET | no |
| paths | [object] | Currently active physical paths (see below) | no |
Path objects:
diff --git a/service/SoftwareUpdater.cpp b/service/SoftwareUpdater.cpp
index 7ec377cc..d12861d4 100644
--- a/service/SoftwareUpdater.cpp
+++ b/service/SoftwareUpdater.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -49,6 +57,35 @@
namespace ZeroTier {
+static int _compareVersion(unsigned int maj1,unsigned int min1,unsigned int rev1,unsigned int b1,unsigned int maj2,unsigned int min2,unsigned int rev2,unsigned int b2)
+{
+ if (maj1 > maj2) {
+ return 1;
+ } else if (maj1 < maj2) {
+ return -1;
+ } else {
+ if (min1 > min2) {
+ return 1;
+ } else if (min1 < min2) {
+ return -1;
+ } else {
+ if (rev1 > rev2) {
+ return 1;
+ } else if (rev1 < rev2) {
+ return -1;
+ } else {
+ if (b1 > b2) {
+ return 1;
+ } else if (b1 < b2) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+ }
+ }
+}
+
SoftwareUpdater::SoftwareUpdater(Node &node,const std::string &homePath) :
_node(node),
_lastCheckTime(0),
@@ -89,11 +126,13 @@ void SoftwareUpdater::setUpdateDistribution(bool distribute)
const std::string binPath(udd + ZT_PATH_SEPARATOR_S + u->substr(0,u->length() - 5));
const std::string metaHash(OSUtils::jsonBinFromHex(d.meta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH]));
if ((metaHash.length() == ZT_SHA512_DIGEST_LEN)&&(OSUtils::readFile(binPath.c_str(),d.bin))) {
- uint8_t sha512[ZT_SHA512_DIGEST_LEN];
- SHA512::hash(sha512,d.bin.data(),(unsigned int)d.bin.length());
- if (!memcmp(sha512,metaHash.data(),ZT_SHA512_DIGEST_LEN)) { // double check that hash in JSON is correct
+ std::array<uint8_t,ZT_SHA512_DIGEST_LEN> sha512;
+ SHA512::hash(sha512.data(),d.bin.data(),(unsigned int)d.bin.length());
+ if (!memcmp(sha512.data(),metaHash.data(),ZT_SHA512_DIGEST_LEN)) { // double check that hash in JSON is correct
d.meta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIZE] = d.bin.length(); // override with correct value -- setting this in meta json is optional
- _dist[Array<uint8_t,16>(sha512)] = d;
+ std::array<uint8_t,16> shakey;
+ memcpy(shakey.data(),sha512.data(),16);
+ _dist[shakey] = d;
if (_distLog) {
fprintf(_distLog,".......... INIT: DISTRIBUTING %s (%u bytes)" ZT_EOL_S,binPath.c_str(),(unsigned int)d.bin.length());
fflush(_distLog);
@@ -142,7 +181,7 @@ void SoftwareUpdater::handleSoftwareUpdateUserMessage(uint64_t origin,const void
unsigned int bestVMin = rvMin;
unsigned int bestVRev = rvRev;
unsigned int bestVBld = rvBld;
- for(std::map< Array<uint8_t,16>,_D >::const_iterator d(_dist.begin());d!=_dist.end();++d) {
+ for(std::map< std::array<uint8_t,16>,_D >::const_iterator d(_dist.begin());d!=_dist.end();++d) {
// The arch field in update description .json files can be an array for e.g. multi-arch update files
const nlohmann::json &dvArch2 = d->second.meta[ZT_SOFTWARE_UPDATE_JSON_ARCHITECTURE];
std::vector<unsigned int> dvArch;
@@ -162,7 +201,7 @@ void SoftwareUpdater::handleSoftwareUpdateUserMessage(uint64_t origin,const void
const unsigned int dvMin = (unsigned int)OSUtils::jsonInt(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_VERSION_MINOR],0);
const unsigned int dvRev = (unsigned int)OSUtils::jsonInt(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_VERSION_REVISION],0);
const unsigned int dvBld = (unsigned int)OSUtils::jsonInt(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_VERSION_BUILD],0);
- if (Utils::compareVersion(dvMaj,dvMin,dvRev,dvBld,bestVMaj,bestVMin,bestVRev,bestVBld) > 0) {
+ if (_compareVersion(dvMaj,dvMin,dvRev,dvBld,bestVMaj,bestVMin,bestVRev,bestVBld) > 0) {
latest = &(d->second.meta);
bestVMaj = dvMaj;
bestVMin = dvMin;
@@ -186,7 +225,7 @@ void SoftwareUpdater::handleSoftwareUpdateUserMessage(uint64_t origin,const void
} else { // VERB_LATEST
if ((origin == ZT_SOFTWARE_UPDATE_SERVICE)&&
- (Utils::compareVersion(rvMaj,rvMin,rvRev,rvBld,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION,ZEROTIER_ONE_VERSION_BUILD) > 0)&&
+ (_compareVersion(rvMaj,rvMin,rvRev,rvBld,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION,ZEROTIER_ONE_VERSION_BUILD) > 0)&&
(OSUtils::jsonString(req[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIGNED_BY],"") == ZT_SOFTWARE_UPDATE_SIGNING_AUTHORITY)) {
const unsigned long len = (unsigned long)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIZE],0);
const std::string hash = OSUtils::jsonBinFromHex(req[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH]);
@@ -196,17 +235,16 @@ void SoftwareUpdater::handleSoftwareUpdateUserMessage(uint64_t origin,const void
_latestValid = false;
OSUtils::rm((_homePath + ZT_PATH_SEPARATOR_S ZT_SOFTWARE_UPDATE_BIN_FILENAME).c_str());
_download = std::string();
- memcpy(_downloadHashPrefix.data,hash.data(),16);
+ memcpy(_downloadHashPrefix.data(),hash.data(),16);
_downloadLength = len;
}
if ((_downloadLength > 0)&&(_download.length() < _downloadLength)) {
Buffer<128> gd;
gd.append((uint8_t)VERB_GET_DATA);
- gd.append(_downloadHashPrefix.data,16);
+ gd.append(_downloadHashPrefix.data(),16);
gd.append((uint32_t)_download.length());
_node.sendUserMessage((void *)0,ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,gd.data(),gd.size());
- //printf(">> GET_DATA @%u\n",(unsigned int)_download.length());
}
}
}
@@ -221,8 +259,9 @@ void SoftwareUpdater::handleSoftwareUpdateUserMessage(uint64_t origin,const void
idx |= (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 18) << 16;
idx |= (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 19) << 8;
idx |= (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 20);
- //printf("<< GET_DATA @%u from %.10llx for %s\n",(unsigned int)idx,origin,Utils::hex(reinterpret_cast<const uint8_t *>(data) + 1,16).c_str());
- std::map< Array<uint8_t,16>,_D >::iterator d(_dist.find(Array<uint8_t,16>(reinterpret_cast<const uint8_t *>(data) + 1)));
+ std::array<uint8_t,16> shakey;
+ memcpy(shakey.data(),reinterpret_cast<const uint8_t *>(data) + 1,16);
+ std::map< std::array<uint8_t,16>,_D >::iterator d(_dist.find(shakey));
if ((d != _dist.end())&&(idx < (unsigned long)d->second.bin.length())) {
Buffer<ZT_SOFTWARE_UPDATE_CHUNK_SIZE + 128> buf;
buf.append((uint8_t)VERB_DATA);
@@ -230,27 +269,24 @@ void SoftwareUpdater::handleSoftwareUpdateUserMessage(uint64_t origin,const void
buf.append((uint32_t)idx);
buf.append(d->second.bin.data() + idx,std::min((unsigned long)ZT_SOFTWARE_UPDATE_CHUNK_SIZE,(unsigned long)(d->second.bin.length() - idx)));
_node.sendUserMessage((void *)0,origin,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,buf.data(),buf.size());
- //printf(">> DATA @%u\n",(unsigned int)idx);
}
}
break;
case VERB_DATA:
- if ((len >= 21)&&(_downloadLength > 0)&&(!memcmp(_downloadHashPrefix.data,reinterpret_cast<const uint8_t *>(data) + 1,16))) {
+ if ((len >= 21)&&(_downloadLength > 0)&&(!memcmp(_downloadHashPrefix.data(),reinterpret_cast<const uint8_t *>(data) + 1,16))) {
unsigned long idx = (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 17) << 24;
idx |= (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 18) << 16;
idx |= (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 19) << 8;
idx |= (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 20);
- //printf("<< DATA @%u / %u bytes (we now have %u bytes)\n",(unsigned int)idx,(unsigned int)(len - 21),(unsigned int)_download.length());
if (idx == (unsigned long)_download.length()) {
_download.append(reinterpret_cast<const char *>(data) + 21,len - 21);
if (_download.length() < _downloadLength) {
Buffer<128> gd;
gd.append((uint8_t)VERB_GET_DATA);
- gd.append(_downloadHashPrefix.data,16);
+ gd.append(_downloadHashPrefix.data(),16);
gd.append((uint32_t)_download.length());
_node.sendUserMessage((void *)0,ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,gd.data(),gd.size());
- //printf(">> GET_DATA @%u\n",(unsigned int)_download.length());
}
}
}
@@ -271,12 +307,12 @@ void SoftwareUpdater::handleSoftwareUpdateUserMessage(uint64_t origin,const void
}
}
-bool SoftwareUpdater::check(const uint64_t now)
+bool SoftwareUpdater::check(const int64_t now)
{
if ((now - _lastCheckTime) >= ZT_SOFTWARE_UPDATE_CHECK_PERIOD) {
_lastCheckTime = now;
char tmp[512];
- const unsigned int len = Utils::snprintf(tmp,sizeof(tmp),
+ const unsigned int len = OSUtils::ztsnprintf(tmp,sizeof(tmp),
"%c{\"" ZT_SOFTWARE_UPDATE_JSON_VERSION_MAJOR "\":%d,"
"\"" ZT_SOFTWARE_UPDATE_JSON_VERSION_MINOR "\":%d,"
"\"" ZT_SOFTWARE_UPDATE_JSON_VERSION_REVISION "\":%d,"
@@ -297,7 +333,6 @@ bool SoftwareUpdater::check(const uint64_t now)
(int)ZT_VENDOR_ZEROTIER,
_channel.c_str());
_node.sendUserMessage((void *)0,ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,tmp,len);
- //printf(">> GET_LATEST\n");
}
if (_latestValid)
@@ -313,7 +348,8 @@ bool SoftwareUpdater::check(const uint64_t now)
// (1) Check the hash itself to make sure the image is basically okay
uint8_t sha512[ZT_SHA512_DIGEST_LEN];
SHA512::hash(sha512,_download.data(),(unsigned int)_download.length());
- if (Utils::hex(sha512,ZT_SHA512_DIGEST_LEN) == OSUtils::jsonString(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH],"")) {
+ char hexbuf[(ZT_SHA512_DIGEST_LEN * 2) + 2];
+ if (OSUtils::jsonString(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH],"") == Utils::hex(sha512,ZT_SHA512_DIGEST_LEN,hexbuf)) {
// (2) Check signature by signing authority
const std::string sig(OSUtils::jsonBinFromHex(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIGNATURE]));
if (Identity(ZT_SOFTWARE_UPDATE_SIGNING_AUTHORITY).verify(_download.data(),(unsigned int)_download.length(),sig.data(),(unsigned int)sig.length())) {
@@ -322,7 +358,6 @@ bool SoftwareUpdater::check(const uint64_t now)
if (OSUtils::writeFile(binPath.c_str(),_download)) {
OSUtils::lockDownFile(binPath.c_str(),false);
_latestValid = true;
- //printf("VALID UPDATE\n%s\n",OSUtils::jsonDump(_latestMeta).c_str());
_download = std::string();
_downloadLength = 0;
return true;
@@ -332,7 +367,6 @@ bool SoftwareUpdater::check(const uint64_t now)
} catch ( ... ) {} // any exception equals verification failure
// If we get here, checks failed.
- //printf("INVALID UPDATE (!!!)\n%s\n",OSUtils::jsonDump(_latestMeta).c_str());
OSUtils::rm(binPath.c_str());
_latestMeta = nlohmann::json();
_latestValid = false;
@@ -341,10 +375,9 @@ bool SoftwareUpdater::check(const uint64_t now)
} else {
Buffer<128> gd;
gd.append((uint8_t)VERB_GET_DATA);
- gd.append(_downloadHashPrefix.data,16);
+ gd.append(_downloadHashPrefix.data(),16);
gd.append((uint32_t)_download.length());
_node.sendUserMessage((void *)0,ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,gd.data(),gd.size());
- //printf(">> GET_DATA @%u\n",(unsigned int)_download.length());
}
}
diff --git a/service/SoftwareUpdater.hpp b/service/SoftwareUpdater.hpp
index 4bb0ef51..7f73eba2 100644
--- a/service/SoftwareUpdater.hpp
+++ b/service/SoftwareUpdater.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_SOFTWAREUPDATER_HPP
@@ -25,11 +33,11 @@
#include <vector>
#include <map>
#include <string>
+#include <array>
#include "../include/ZeroTierOne.h"
#include "../node/Identity.hpp"
-#include "../node/Array.hpp"
#include "../node/Packet.hpp"
#include "../ext/json/json.hpp"
@@ -159,7 +167,7 @@ public:
*
* @return True if we've downloaded and verified an update
*/
- bool check(const uint64_t now);
+ bool check(const int64_t now);
/**
* @return Meta-data for downloaded update or NULL if none
@@ -194,13 +202,13 @@ private:
nlohmann::json meta;
std::string bin;
};
- std::map< Array<uint8_t,16>,_D > _dist; // key is first 16 bytes of hash
+ std::map< std::array<uint8_t,16>,_D > _dist; // key is first 16 bytes of hash
nlohmann::json _latestMeta;
bool _latestValid;
std::string _download;
- Array<uint8_t,16> _downloadHashPrefix;
+ std::array<uint8_t,16> _downloadHashPrefix;
unsigned long _downloadLength;
};
diff --git a/tcp-proxy/Makefile b/tcp-proxy/Makefile
deleted file mode 100644
index af4e71e3..00000000
--- a/tcp-proxy/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-CXX=$(shell which clang++ g++ c++ 2>/dev/null | head -n 1)
-
-all:
- $(CXX) -O3 -fno-rtti -o tcp-proxy tcp-proxy.cpp
-
-clean:
- rm -f *.o tcp-proxy *.dSYM
diff --git a/tcp-proxy/README.md b/tcp-proxy/README.md
deleted file mode 100644
index 6f347d64..00000000
--- a/tcp-proxy/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-TCP Proxy Server
-======
-
-This is the TCP proxy server we run for TCP tunneling from peers behind fascist NATs. Regular users won't have much use for this.
diff --git a/tcp-proxy/tcp-proxy.cpp b/tcp-proxy/tcp-proxy.cpp
deleted file mode 100644
index a7906aae..00000000
--- a/tcp-proxy/tcp-proxy.cpp
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-// HACK! Will eventually use epoll() or something in Phy<> instead of select().
-// Also be sure to change ulimit -n and fs.file-max in /etc/sysctl.conf on relays.
-#if defined(__linux__) || defined(__LINUX__) || defined(__LINUX) || defined(LINUX)
-#include <linux/posix_types.h>
-#include <bits/types.h>
-#undef __FD_SETSIZE
-#define __FD_SETSIZE 1048576
-#undef FD_SETSIZE
-#define FD_SETSIZE 1048576
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <signal.h>
-
-#include <map>
-#include <set>
-#include <string>
-#include <algorithm>
-#include <vector>
-
-#include "../osdep/Phy.hpp"
-
-#define ZT_TCP_PROXY_CONNECTION_TIMEOUT_SECONDS 300
-#define ZT_TCP_PROXY_TCP_PORT 443
-
-using namespace ZeroTier;
-
-/*
- * ZeroTier TCP Proxy Server
- *
- * This implements a simple packet encapsulation that is designed to look like
- * a TLS connection. It's not a TLS connection, but it sends TLS format record
- * headers. It could be extended in the future to implement a fake TLS
- * handshake.
- *
- * At the moment, each packet is just made to look like TLS application data:
- * <[1] TLS content type> - currently 0x17 for "application data"
- * <[1] TLS major version> - currently 0x03 for TLS 1.2
- * <[1] TLS minor version> - currently 0x03 for TLS 1.2
- * <[2] payload length> - 16-bit length of payload in bytes
- * <[...] payload> - Message payload
- *
- * TCP is inherently inefficient for encapsulating Ethernet, since TCP and TCP
- * like protocols over TCP lead to double-ACKs. So this transport is only used
- * to enable access when UDP or other datagram protocols are not available.
- *
- * Clients send a greeting, which is a four-byte message that contains:
- * <[1] ZeroTier major version>
- * <[1] minor version>
- * <[2] revision>
- *
- * If a client has sent a greeting, it uses the new version of this protocol
- * in which every encapsulated ZT packet is prepended by an IP address where
- * it should be forwarded (or where it came from for replies). This causes
- * this proxy to act as a remote UDP socket similar to a socks proxy, which
- * will allow us to move this function off the rootservers and onto dedicated
- * proxy nodes.
- *
- * Older ZT clients that do not send this message get their packets relayed
- * to/from 127.0.0.1:9993, which will allow them to talk to and relay via
- * the ZT node on the same machine as the proxy. We'll only support this for
- * as long as such nodes appear to be in the wild.
- */
-
-struct TcpProxyService;
-struct TcpProxyService
-{
- Phy<TcpProxyService *> *phy;
- int udpPortCounter;
- struct Client
- {
- char tcpReadBuf[131072];
- char tcpWriteBuf[131072];
- unsigned long tcpWritePtr;
- unsigned long tcpReadPtr;
- PhySocket *tcp;
- PhySocket *udp;
- time_t lastActivity;
- bool newVersion;
- };
- std::map< PhySocket *,Client > clients;
-
- PhySocket *getUnusedUdp(void *uptr)
- {
- for(int i=0;i<65535;++i) {
- ++udpPortCounter;
- if (udpPortCounter > 0xfffe)
- udpPortCounter = 1024;
- struct sockaddr_in laddr;
- memset(&laddr,0,sizeof(struct sockaddr_in));
- laddr.sin_family = AF_INET;
- laddr.sin_port = htons((uint16_t)udpPortCounter);
- PhySocket *udp = phy->udpBind(reinterpret_cast<struct sockaddr *>(&laddr),uptr);
- if (udp)
- return udp;
- }
- return (PhySocket *)0;
- }
-
- void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len)
- {
- if (!*uptr)
- return;
- if ((from->sa_family == AF_INET)&&(len >= 16)&&(len < 2048)) {
- Client &c = *((Client *)*uptr);
- c.lastActivity = time((time_t *)0);
-
- unsigned long mlen = len;
- if (c.newVersion)
- mlen += 7; // new clients get IP info
-
- if ((c.tcpWritePtr + 5 + mlen) <= sizeof(c.tcpWriteBuf)) {
- if (!c.tcpWritePtr)
- phy->setNotifyWritable(c.tcp,true);
-
- c.tcpWriteBuf[c.tcpWritePtr++] = 0x17; // look like TLS data
- c.tcpWriteBuf[c.tcpWritePtr++] = 0x03; // look like TLS 1.2
- c.tcpWriteBuf[c.tcpWritePtr++] = 0x03; // look like TLS 1.2
-
- c.tcpWriteBuf[c.tcpWritePtr++] = (char)((mlen >> 8) & 0xff);
- c.tcpWriteBuf[c.tcpWritePtr++] = (char)(mlen & 0xff);
-
- if (c.newVersion) {
- c.tcpWriteBuf[c.tcpWritePtr++] = (char)4; // IPv4
- *((uint32_t *)(c.tcpWriteBuf + c.tcpWritePtr)) = ((const struct sockaddr_in *)from)->sin_addr.s_addr;
- c.tcpWritePtr += 4;
- *((uint16_t *)(c.tcpWriteBuf + c.tcpWritePtr)) = ((const struct sockaddr_in *)from)->sin_port;
- c.tcpWritePtr += 2;
- }
-
- for(unsigned long i=0;i<len;++i)
- c.tcpWriteBuf[c.tcpWritePtr++] = ((const char *)data)[i];
- }
-
- //printf("<< UDP %s:%d -> %.16llx\n",inet_ntoa(reinterpret_cast<const struct sockaddr_in *>(from)->sin_addr),(int)ntohs(reinterpret_cast<const struct sockaddr_in *>(from)->sin_port),(unsigned long long)&c);
- }
- }
-
- void phyOnTcpConnect(PhySocket *sock,void **uptr,bool success)
- {
- // unused, we don't initiate outbound connections
- }
-
- void phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from)
- {
- Client &c = clients[sockN];
- PhySocket *udp = getUnusedUdp((void *)&c);
- if (!udp) {
- phy->close(sockN);
- clients.erase(sockN);
- //printf("** TCP rejected, no more UDP ports to assign\n");
- return;
- }
- c.tcpWritePtr = 0;
- c.tcpReadPtr = 0;
- c.tcp = sockN;
- c.udp = udp;
- c.lastActivity = time((time_t *)0);
- c.newVersion = false;
- *uptrN = (void *)&c;
- //printf("<< TCP from %s -> %.16llx\n",inet_ntoa(reinterpret_cast<const struct sockaddr_in *>(from)->sin_addr),(unsigned long long)&c);
- }
-
- void phyOnTcpClose(PhySocket *sock,void **uptr)
- {
- if (!*uptr)
- return;
- Client &c = *((Client *)*uptr);
- phy->close(c.udp);
- clients.erase(sock);
- //printf("** TCP %.16llx closed\n",(unsigned long long)*uptr);
- }
-
- void phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len)
- {
- Client &c = *((Client *)*uptr);
- c.lastActivity = time((time_t *)0);
-
- for(unsigned long i=0;i<len;++i) {
- if (c.tcpReadPtr >= sizeof(c.tcpReadBuf)) {
- phy->close(sock);
- return;
- }
- c.tcpReadBuf[c.tcpReadPtr++] = ((const char *)data)[i];
-
- if (c.tcpReadPtr >= 5) {
- unsigned long mlen = ( ((((unsigned long)c.tcpReadBuf[3]) & 0xff) << 8) | (((unsigned long)c.tcpReadBuf[4]) & 0xff) );
- if (c.tcpReadPtr >= (mlen + 5)) {
- if (mlen == 4) {
- // Right now just sending this means the client is 'new enough' for the IP header
- c.newVersion = true;
- //printf("<< TCP %.16llx HELLO\n",(unsigned long long)*uptr);
- } else if (mlen >= 7) {
- char *payload = c.tcpReadBuf + 5;
- unsigned long payloadLen = mlen;
-
- struct sockaddr_in dest;
- memset(&dest,0,sizeof(dest));
- if (c.newVersion) {
- if (*payload == (char)4) {
- // New clients tell us where their packets go.
- ++payload;
- dest.sin_family = AF_INET;
- dest.sin_addr.s_addr = *((uint32_t *)payload);
- payload += 4;
- dest.sin_port = *((uint16_t *)payload); // will be in network byte order already
- payload += 2;
- payloadLen -= 7;
- }
- } else {
- // For old clients we will just proxy everything to a local ZT instance. The
- // fact that this will come from 127.0.0.1 will in turn prevent that instance
- // from doing unite() with us. It'll just forward. There will not be many of
- // these.
- dest.sin_family = AF_INET;
- dest.sin_addr.s_addr = htonl(0x7f000001); // 127.0.0.1
- dest.sin_port = htons(9993);
- }
-
- // Note: we do not relay to privileged ports... just an abuse prevention rule.
- if ((ntohs(dest.sin_port) > 1024)&&(payloadLen >= 16)) {
- phy->udpSend(c.udp,(const struct sockaddr *)&dest,payload,payloadLen);
- //printf(">> TCP %.16llx to %s:%d\n",(unsigned long long)*uptr,inet_ntoa(dest.sin_addr),(int)ntohs(dest.sin_port));
- }
- }
-
- memmove(c.tcpReadBuf,c.tcpReadBuf + (mlen + 5),c.tcpReadPtr -= (mlen + 5));
- }
- }
- }
- }
-
- void phyOnTcpWritable(PhySocket *sock,void **uptr)
- {
- Client &c = *((Client *)*uptr);
- if (c.tcpWritePtr) {
- long n = phy->streamSend(sock,c.tcpWriteBuf,c.tcpWritePtr);
- if (n > 0) {
- memmove(c.tcpWriteBuf,c.tcpWriteBuf + n,c.tcpWritePtr -= (unsigned long)n);
- if (!c.tcpWritePtr)
- phy->setNotifyWritable(sock,false);
- }
- } else phy->setNotifyWritable(sock,false);
- }
-
- void doHousekeeping()
- {
- std::vector<PhySocket *> toClose;
- time_t now = time((time_t *)0);
- for(std::map< PhySocket *,Client >::iterator c(clients.begin());c!=clients.end();++c) {
- if ((now - c->second.lastActivity) >= ZT_TCP_PROXY_CONNECTION_TIMEOUT_SECONDS) {
- toClose.push_back(c->first);
- toClose.push_back(c->second.udp);
- }
- }
- for(std::vector<PhySocket *>::iterator s(toClose.begin());s!=toClose.end();++s)
- phy->close(*s);
- }
-};
-
-int main(int argc,char **argv)
-{
- signal(SIGPIPE,SIG_IGN);
- signal(SIGHUP,SIG_IGN);
- srand(time((time_t *)0));
-
- TcpProxyService svc;
- Phy<TcpProxyService *> phy(&svc,false,true);
- svc.phy = &phy;
- svc.udpPortCounter = 1023;
-
- {
- struct sockaddr_in laddr;
- memset(&laddr,0,sizeof(laddr));
- laddr.sin_family = AF_INET;
- laddr.sin_port = htons(ZT_TCP_PROXY_TCP_PORT);
- if (!phy.tcpListen((const struct sockaddr *)&laddr)) {
- fprintf(stderr,"%s: fatal error: unable to bind TCP port %d\n",argv[0],ZT_TCP_PROXY_TCP_PORT);
- return 1;
- }
- }
-
- time_t lastDidHousekeeping = time((time_t *)0);
- for(;;) {
- phy.poll(120000);
- time_t now = time((time_t *)0);
- if ((now - lastDidHousekeeping) > 120) {
- lastDidHousekeeping = now;
- svc.doHousekeeping();
- }
- }
-
- return 0;
-}
diff --git a/version.h b/version.h
index d20f6148..064f2585 100644
--- a/version.h
+++ b/version.h
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef _ZT_VERSION_H
@@ -32,7 +40,7 @@
/**
* Revision
*/
-#define ZEROTIER_ONE_VERSION_REVISION 4
+#define ZEROTIER_ONE_VERSION_REVISION 6
/**
* Build version
diff --git a/windows/WinUI/APIHandler.cs b/windows/WinUI/APIHandler.cs
index 419a11cd..015415c3 100644
--- a/windows/WinUI/APIHandler.cs
+++ b/windows/WinUI/APIHandler.cs
@@ -8,6 +8,7 @@ using System.IO;
using System.Windows;
using Newtonsoft.Json;
using System.Diagnostics;
+using System.Windows.Threading;
namespace WinUI
{
@@ -25,6 +26,8 @@ namespace WinUI
public delegate void NetworkListCallback(List<ZeroTierNetwork> networks);
public delegate void StatusCallback(ZeroTierStatus status);
+ private string ZeroTierAddress = "";
+
public static APIHandler Instance
{
get
@@ -169,6 +172,11 @@ namespace WinUI
try
{
status = JsonConvert.DeserializeObject<ZeroTierStatus>(responseText);
+
+ if (ZeroTierAddress != status.Address)
+ {
+ ZeroTierAddress = status.Address;
+ }
}
catch (JsonReaderException e)
{
@@ -188,7 +196,8 @@ namespace WinUI
}
catch (System.Net.WebException e)
{
- if (((HttpWebResponse)e.Response).StatusCode == HttpStatusCode.Unauthorized)
+ HttpWebResponse res = (HttpWebResponse)e.Response;
+ if (res != null && res.StatusCode == HttpStatusCode.Unauthorized)
{
APIHandler.initHandler(true);
}
@@ -251,7 +260,8 @@ namespace WinUI
}
catch (System.Net.WebException e)
{
- if (((HttpWebResponse)e.Response).StatusCode == HttpStatusCode.Unauthorized)
+ HttpWebResponse res = (HttpWebResponse)e.Response;
+ if (res != null && res.StatusCode == HttpStatusCode.Unauthorized)
{
APIHandler.initHandler(true);
}
@@ -262,102 +272,125 @@ namespace WinUI
}
}
- public void JoinNetwork(string nwid, bool allowManaged = true, bool allowGlobal = false, bool allowDefault = false)
+ public void JoinNetwork(Dispatcher d, string nwid, bool allowManaged = true, bool allowGlobal = false, bool allowDefault = false)
{
- var request = WebRequest.Create(url + "/network/" + nwid + "?auth=" + authtoken) as HttpWebRequest;
- if (request == null)
- {
- return;
- }
-
- request.Method = "POST";
- request.ContentType = "applicaiton/json";
- request.Timeout = 10000;
- try
+ Task.Factory.StartNew(() =>
{
- using (var streamWriter = new StreamWriter(((HttpWebRequest)request).GetRequestStream()))
+ var request = WebRequest.Create(url + "/network/" + nwid + "?auth=" + authtoken) as HttpWebRequest;
+ if (request == null)
{
- string json = "{\"allowManaged\":" + (allowManaged ? "true" : "false") + "," +
- "\"allowGlobal\":" + (allowGlobal ? "true" : "false") + "," +
- "\"allowDefault\":" + (allowDefault ? "true" : "false") + "}";
- streamWriter.Write(json);
- streamWriter.Flush();
- streamWriter.Close();
+ return;
}
- }
- catch (System.Net.WebException)
- {
- MessageBox.Show("Error Joining Network: Cannot connect to ZeroTier service.");
- return;
- }
- try
- {
- var httpResponse = (HttpWebResponse)request.GetResponse();
+ request.Method = "POST";
+ request.ContentType = "applicaiton/json";
+ request.Timeout = 30000;
+ try
+ {
+ using (var streamWriter = new StreamWriter(((HttpWebRequest)request).GetRequestStream()))
+ {
+ string json = "{\"allowManaged\":" + (allowManaged ? "true" : "false") + "," +
+ "\"allowGlobal\":" + (allowGlobal ? "true" : "false") + "," +
+ "\"allowDefault\":" + (allowDefault ? "true" : "false") + "}";
+ streamWriter.Write(json);
+ streamWriter.Flush();
+ streamWriter.Close();
+ }
+ }
+ catch (System.Net.WebException)
+ {
+ d.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ MessageBox.Show("Error Joining Network: Cannot connect to ZeroTier service.");
+ }));
+ return;
+ }
- if (httpResponse.StatusCode == HttpStatusCode.Unauthorized)
+ try
{
- APIHandler.initHandler(true);
+ var httpResponse = (HttpWebResponse)request.GetResponse();
+
+ if (httpResponse.StatusCode == HttpStatusCode.Unauthorized)
+ {
+ APIHandler.initHandler(true);
+ }
+ else if (httpResponse.StatusCode != HttpStatusCode.OK)
+ {
+ Console.WriteLine("Error sending join network message");
+ }
}
- else if (httpResponse.StatusCode != HttpStatusCode.OK)
- {
- Console.WriteLine("Error sending join network message");
- }
- }
- catch (System.Net.Sockets.SocketException)
- {
- MessageBox.Show("Error Joining Network: Cannot connect to ZeroTier service.");
- }
- catch (System.Net.WebException e)
- {
- if (((HttpWebResponse)e.Response).StatusCode == HttpStatusCode.Unauthorized)
+ catch (System.Net.Sockets.SocketException)
{
- APIHandler.initHandler(true);
+ d.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ MessageBox.Show("Error Joining Network: Cannot connect to ZeroTier service.");
+ }));
}
- MessageBox.Show("Error Joining Network: Cannot connect to ZeroTier service.");
- }
+ catch (System.Net.WebException e)
+ {
+ HttpWebResponse res = (HttpWebResponse)e.Response;
+ if (res != null && res.StatusCode == HttpStatusCode.Unauthorized)
+ {
+ APIHandler.initHandler(true);
+ }
+ d.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ MessageBox.Show("Error Joining Network: Cannot connect to ZeroTier service.");
+ }));
+ }
+ });
}
- public void LeaveNetwork(string nwid)
+ public void LeaveNetwork(Dispatcher d, string nwid)
{
- var request = WebRequest.Create(url + "/network/" + nwid + "?auth=" + authtoken) as HttpWebRequest;
- if (request == null)
- {
- return;
- }
+ Task.Factory.StartNew(() =>
+ {
+ var request = WebRequest.Create(url + "/network/" + nwid + "?auth=" + authtoken) as HttpWebRequest;
+ if (request == null)
+ {
+ return;
+ }
- request.Method = "DELETE";
- request.Timeout = 10000;
+ request.Method = "DELETE";
+ request.Timeout = 30000;
- try
- {
- var httpResponse = (HttpWebResponse)request.GetResponse();
+ try
+ {
+ var httpResponse = (HttpWebResponse)request.GetResponse();
- if (httpResponse.StatusCode == HttpStatusCode.Unauthorized)
+ if (httpResponse.StatusCode == HttpStatusCode.Unauthorized)
+ {
+ APIHandler.initHandler(true);
+ }
+ else if (httpResponse.StatusCode != HttpStatusCode.OK)
+ {
+ Console.WriteLine("Error sending leave network message");
+ }
+ }
+ catch (System.Net.Sockets.SocketException)
{
- APIHandler.initHandler(true);
+ d.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ MessageBox.Show("Error Leaving Network: Cannot connect to ZeroTier service.");
+ }));
}
- else if (httpResponse.StatusCode != HttpStatusCode.OK)
- {
- Console.WriteLine("Error sending leave network message");
- }
- }
- catch (System.Net.Sockets.SocketException)
- {
- MessageBox.Show("Error Leaving Network: Cannot connect to ZeroTier service.");
- }
- catch (System.Net.WebException e)
- {
- if (((HttpWebResponse)e.Response).StatusCode == HttpStatusCode.Unauthorized)
+ catch (System.Net.WebException e)
{
- APIHandler.initHandler(true);
+ HttpWebResponse res = (HttpWebResponse)e.Response;
+ if (res != null && res.StatusCode == HttpStatusCode.Unauthorized)
+ {
+ APIHandler.initHandler(true);
+ }
+ d.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ MessageBox.Show("Error Leaving Network: Cannot connect to ZeroTier service.");
+ }));
}
- MessageBox.Show("Error Leaving Network: Cannot connect to ZeroTier service.");
- }
- catch
- {
- Console.WriteLine("Error leaving network: Unknown error");
- }
+ catch
+ {
+ Console.WriteLine("Error leaving network: Unknown error");
+ }
+ });
}
public delegate void PeersCallback(List<ZeroTierPeer> peers);
@@ -405,7 +438,8 @@ namespace WinUI
}
catch (System.Net.WebException e)
{
- if (((HttpWebResponse)e.Response).StatusCode == HttpStatusCode.Unauthorized)
+ HttpWebResponse res = (HttpWebResponse)e.Response;
+ if (res != null && res.StatusCode == HttpStatusCode.Unauthorized)
{
APIHandler.initHandler(true);
}
@@ -415,5 +449,10 @@ namespace WinUI
}
}
}
+
+ public string NodeAddress()
+ {
+ return ZeroTierAddress;
+ }
}
}
diff --git a/windows/WinUI/AboutView.xaml b/windows/WinUI/AboutView.xaml
index f207af4c..39213b73 100644
--- a/windows/WinUI/AboutView.xaml
+++ b/windows/WinUI/AboutView.xaml
@@ -14,22 +14,22 @@
<Setter Property="Cursor" Value="Hand" />
</Style>
</RichTextBox.Resources>
- <FlowDocument>
- <Paragraph TextAlignment="Center">
- <Run Text="ZeroTier One"/>
- </Paragraph>
- <Paragraph TextAlignment="Center">
- <Run FontSize="14" Text="Version 1.2.4"/>
- <LineBreak/>
- <Run FontSize="14" Text="(c) 2011-2017 ZeroTier, Inc."/>
- <LineBreak/>
- <Run FontSize="14" Text="www.zerotier.com"/>
- </Paragraph>
- <Paragraph TextAlignment="Center">
- <Run FontSize="14" Text="ZeroTier One allows your computer to join virtual networks. Just select &quot;join&quot; and enter a network's 16-digit ID. Each network appears on your computer as a new network port."/>
- </Paragraph>
- </FlowDocument>
- </RichTextBox>
+ <FlowDocument>
+ <Paragraph TextAlignment="Center">
+ <Run Text="ZeroTier One"/>
+ </Paragraph>
+ <Paragraph TextAlignment="Center">
+ <Run FontSize="14" Text="Version 1.2.6"/>
+ <LineBreak/>
+ <Run FontSize="14" Text="(c) 2011-2017 ZeroTier, Inc."/>
+ <LineBreak/>
+ <Run FontSize="14" Text="www.zerotier.com"/>
+ </Paragraph>
+ <Paragraph TextAlignment="Center">
+ <Run FontSize="14" Text="ZeroTier One allows your computer to join virtual networks. Just select &quot;join&quot; and enter a network's 16-digit ID. Each network appears on your computer as a new network port."/>
+ </Paragraph>
+ </FlowDocument>
+ </RichTextBox>
</Grid>
</Window>
diff --git a/windows/WinUI/CentralAPI.cs b/windows/WinUI/CentralAPI.cs
new file mode 100644
index 00000000..fc37aedf
--- /dev/null
+++ b/windows/WinUI/CentralAPI.cs
@@ -0,0 +1,245 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace WinUI
+{
+ class CentralAPI
+ {
+ private static volatile CentralAPI instance;
+ private static object syncRoot = new Object();
+
+ private CookieContainer cookieContainer;
+ private HttpClientHandler clientHandler;
+ private HttpClient client;
+
+ private CentralServer server;
+ public CentralServer Central
+ {
+ get
+ {
+ return this.server;
+ }
+ set
+ {
+ this.server = value;
+ WriteCentralConfig();
+ UpdateRequestHeaders();
+ }
+ }
+
+ public static CentralAPI Instance
+ {
+ get
+ {
+ if (instance == null)
+ {
+ lock (syncRoot)
+ {
+ if (instance == null)
+ {
+ instance = new CentralAPI();
+ }
+ }
+ }
+
+ return instance;
+ }
+ }
+
+
+
+ private CentralAPI()
+ {
+#if DEBUG
+ ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
+#endif
+ cookieContainer = new CookieContainer();
+ clientHandler = new HttpClientHandler
+ {
+ AllowAutoRedirect = true,
+ UseCookies = true,
+ CookieContainer = cookieContainer
+ };
+
+ client = new HttpClient(clientHandler);
+
+ string centralConfigPath = CentralConfigFile();
+ if (File.Exists(centralConfigPath))
+ {
+ byte[] tmp = File.ReadAllBytes(centralConfigPath);
+ string json = Encoding.UTF8.GetString(tmp).Trim();
+ Central = JsonConvert.DeserializeObject<CentralServer>(json);
+ }
+ else
+ {
+ Central = new CentralServer();
+ }
+ }
+
+ public bool HasAccessToken()
+ {
+ if (Central == null)
+ return false;
+
+ return !string.IsNullOrEmpty(Central.APIKey);
+ }
+
+ private string ZeroTierDir()
+ {
+ return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\ZeroTier\\One";
+ }
+
+ private string CentralConfigFile()
+ {
+ return ZeroTierDir() + "\\central.conf";
+ }
+
+ public void WriteCentralConfig()
+ {
+ string json = JsonConvert.SerializeObject(Central);
+ byte[] tmp = Encoding.UTF8.GetBytes(json);
+ File.WriteAllBytes(CentralConfigFile(), tmp);
+ }
+
+ private void UpdateRequestHeaders()
+ {
+ if (client.DefaultRequestHeaders.Contains("Authorization"))
+ {
+ client.DefaultRequestHeaders.Remove("Authorization");
+ }
+
+ if (!string.IsNullOrEmpty(Central.APIKey))
+ {
+ client.DefaultRequestHeaders.Add("Authorization", "bearer " + Central.APIKey);
+ }
+ }
+
+ public async Task<bool> Login(string email, string password, bool isNewUser)
+ {
+ string postURL = Central.ServerURL + "/api/_auth/local";
+ CentralLogin login = new CentralLogin(email, password, isNewUser);
+ var content = new StringContent(JsonConvert.SerializeObject(login), Encoding.UTF8, "application/json");
+ HttpResponseMessage response = await client.PostAsync(postURL, content);
+
+ if (!response.IsSuccessStatusCode)
+ {
+ return false;
+ }
+
+ string resContent = await response.Content.ReadAsStringAsync();
+
+ CentralUser user = JsonConvert.DeserializeObject<CentralUser>(resContent);
+
+ if (user.Tokens.Count == 0)
+ {
+ // create token
+ user = await CreateAuthToken(user);
+ }
+
+ Central.APIKey = user.Tokens[0];
+
+ UpdateRequestHeaders();
+ WriteCentralConfig();
+
+ return true;
+ }
+
+ public async Task<CentralUser> CreateAuthToken(CentralUser user)
+ {
+ string randomTokenURL = Central.ServerURL + "/api/randomToken";
+ HttpResponseMessage response = await client.GetAsync(randomTokenURL);
+
+ if (!response.IsSuccessStatusCode)
+ {
+ // TODO: throw an error
+ return null;
+ }
+
+ string resContent = await response.Content.ReadAsStringAsync();
+
+ CentralToken t = JsonConvert.DeserializeObject<CentralToken>(resContent);
+
+ user.Tokens.Add(t.Token);
+
+ string tokenObj = "{ \"tokens\": " + JsonConvert.SerializeObject(user.Tokens) + " } ";
+
+ string postURL = Central.ServerURL + "/api/user/" + user.Id;
+ var postContent = new StringContent(tokenObj, Encoding.UTF8, "application/json");
+ response = await client.PostAsync(postURL, postContent);
+
+ if (!response.IsSuccessStatusCode)
+ {
+ // TODO: thrown an error
+ return null;
+ }
+
+ resContent = await response.Content.ReadAsStringAsync();
+ user = JsonConvert.DeserializeObject<CentralUser>(resContent);
+
+ return user;
+ }
+
+ public async Task<List<CentralNetwork>> GetNetworkList()
+ {
+ string networkURL = Central.ServerURL + "/api/network";
+
+ HttpResponseMessage response = await client.GetAsync(networkURL);
+
+ if (!response.IsSuccessStatusCode)
+ {
+ // TODO: Throw Error
+ return new List<CentralNetwork>();
+ }
+
+ string resContent = await response.Content.ReadAsStringAsync();
+
+ List<CentralNetwork> networkList = JsonConvert.DeserializeObject<List<CentralNetwork>>(resContent);
+
+ return networkList;
+ }
+
+ public async Task<CentralNetwork> CreateNewNetwork()
+ {
+ string networkURL = Central.ServerURL + "/api/network/";
+ CentralNetwork network = new CentralNetwork();
+ network.Config = new CentralNetwork.CentralNetworkConfig();
+ network.Config.Name = NetworkNameGenerator.GenerateName();
+ string jsonNetwork = JsonConvert.SerializeObject(network);
+ var postContent = new StringContent(jsonNetwork, Encoding.UTF8, "application/json");
+ HttpResponseMessage response = await client.PostAsync(networkURL, postContent);
+
+ if (!response.IsSuccessStatusCode)
+ {
+ return null;
+ }
+
+ string resContent = await response.Content.ReadAsStringAsync();
+
+ CentralNetwork newNetwork = JsonConvert.DeserializeObject<CentralNetwork>(resContent);
+
+ return newNetwork;
+ }
+
+ public async Task<bool> AuthorizeNode(string nodeAddress, string networkId)
+ {
+ string json = "{ \"config\": { \"authorized\": true } }";
+ string postURL = Central.ServerURL + "/api/network/" + networkId + "/member/" + nodeAddress;
+ var postContent = new StringContent(json, Encoding.UTF8, "application/json");
+ HttpResponseMessage response = await client.PostAsync(postURL, postContent);
+
+ if (response.IsSuccessStatusCode)
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/windows/WinUI/CentralLogin.cs b/windows/WinUI/CentralLogin.cs
new file mode 100644
index 00000000..97265dcf
--- /dev/null
+++ b/windows/WinUI/CentralLogin.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace WinUI
+{
+ class CentralLogin
+ {
+
+
+ public CentralLogin(string email, string password, bool isNew)
+ {
+ Login = email;
+ Password = password;
+ IsNew = isNew;
+ }
+
+ [JsonProperty("login")]
+ public string Login { get; set; }
+
+ [JsonProperty("password")]
+ public string Password { get; set; }
+
+ [JsonProperty("register")]
+ public bool IsNew { get; set; }
+ }
+}
diff --git a/windows/WinUI/CentralNetwork.cs b/windows/WinUI/CentralNetwork.cs
new file mode 100644
index 00000000..26ad5234
--- /dev/null
+++ b/windows/WinUI/CentralNetwork.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace WinUI
+{
+ class CentralNetwork
+ {
+ [JsonProperty("id")]
+ public string Id { get; set; }
+
+ [JsonProperty("type")]
+ public string Type { get; set; }
+
+ [JsonProperty("clock")]
+ public UInt64 Clock { get; set; }
+
+ [JsonProperty("rulesSource")]
+ public string RulesSource { get; set; }
+
+ [JsonProperty("description")]
+ public string Description { get; set; }
+
+ [JsonProperty("ownerId")]
+ public string OwnerID { get; set; }
+
+ [JsonProperty("onlineMemberCount")]
+ public int OnlineMemberCount { get; set; }
+
+ [JsonProperty("config")]
+ public CentralNetworkConfig Config { get; set; }
+
+ public class CentralNetworkConfig
+ {
+ [JsonProperty("id")]
+ public string Id { get; set; }
+
+ [JsonProperty("nwid")]
+ public string NetworkID { get; set; }
+
+ [JsonProperty("name")]
+ public string Name { get; set; }
+ }
+ }
+}
diff --git a/windows/WinUI/CentralServer.cs b/windows/WinUI/CentralServer.cs
new file mode 100644
index 00000000..8e268653
--- /dev/null
+++ b/windows/WinUI/CentralServer.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace WinUI
+{
+ class CentralServer
+ {
+ public CentralServer()
+ {
+ ServerURL = "https://my.zerotier.com";
+ }
+
+ [JsonProperty("server_url")]
+ public string ServerURL { get; set; }
+
+ [JsonProperty("api_key")]
+ public string APIKey { get; set; }
+ }
+}
diff --git a/windows/WinUI/CentralToken.cs b/windows/WinUI/CentralToken.cs
new file mode 100644
index 00000000..1db548aa
--- /dev/null
+++ b/windows/WinUI/CentralToken.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace WinUI
+{
+ class CentralToken
+ {
+ [JsonProperty("token")]
+ public string Token { get; set; }
+
+ [JsonProperty("clock")]
+ public UInt64 Clock { get; set; }
+
+ [JsonProperty("raw")]
+ public string Raw { get; set; }
+ }
+}
diff --git a/windows/WinUI/CentralUser.cs b/windows/WinUI/CentralUser.cs
new file mode 100644
index 00000000..8a8945a2
--- /dev/null
+++ b/windows/WinUI/CentralUser.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace WinUI
+{
+ class CentralUser
+ {
+ public class CentralGlobalPermissions
+ {
+ [JsonProperty("a")]
+ public bool Administrator { get; set; }
+
+ [JsonProperty("d")]
+ public bool Delete { get; set; }
+
+ [JsonProperty("m")]
+ public bool Modify { get; set; }
+
+ [JsonProperty("r")]
+ public bool Read { get; set; }
+ }
+
+ [JsonProperty("id")]
+ public string Id { get; set; }
+
+ [JsonProperty("type")]
+ public string Type { get; set; }
+
+ [JsonProperty("clock")]
+ public UInt64 Clock { get; set; }
+
+ [JsonProperty("globalPermissions")]
+ public CentralGlobalPermissions GlobalPermissions { get; set; }
+
+ [JsonProperty("displayName")]
+ public string DisplayName { get; set; }
+
+ [JsonProperty("email")]
+ public string Email { get; set; }
+
+ [JsonProperty("smsNumber")]
+ public string SmsNumber { get; set; }
+
+ [JsonProperty("tokens")]
+ public List<string> Tokens { get; set; }
+ }
+}
diff --git a/windows/WinUI/ISwitchable.cs b/windows/WinUI/ISwitchable.cs
new file mode 100644
index 00000000..e485a14c
--- /dev/null
+++ b/windows/WinUI/ISwitchable.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WinUI
+{
+ interface ISwitchable
+ {
+ void UtilizeState(object state);
+ }
+}
diff --git a/windows/WinUI/JoinNetworkView.xaml.cs b/windows/WinUI/JoinNetworkView.xaml.cs
index 548a51e6..0b4ac324 100644
--- a/windows/WinUI/JoinNetworkView.xaml.cs
+++ b/windows/WinUI/JoinNetworkView.xaml.cs
@@ -118,7 +118,7 @@ namespace WinUI
bool allowGlobal = allowGlobalCheckbox.IsChecked.Value;
bool allowManaged = allowManagedCheckbox.IsChecked.Value;
- APIHandler.Instance.JoinNetwork(joinNetworkBox.Text, allowManaged, allowGlobal, allowDefault);
+ APIHandler.Instance.JoinNetwork(this.Dispatcher, joinNetworkBox.Text, allowManaged, allowGlobal, allowDefault);
Close();
}
diff --git a/windows/WinUI/NetworkInfoView.xaml.cs b/windows/WinUI/NetworkInfoView.xaml.cs
index 1f99a1fe..51f76753 100644
--- a/windows/WinUI/NetworkInfoView.xaml.cs
+++ b/windows/WinUI/NetworkInfoView.xaml.cs
@@ -106,14 +106,14 @@ namespace WinUI
private void deleteButton_Click(object sender, RoutedEventArgs e)
{
- APIHandler.Instance.LeaveNetwork(network.NetworkId);
+ APIHandler.Instance.LeaveNetwork(this.Dispatcher, network.NetworkId);
NetworkMonitor.Instance.RemoveNetwork(network.NetworkId);
}
private void AllowManaged_CheckStateChanged(object sender, RoutedEventArgs e)
{
CheckBox cb = sender as CheckBox;
- APIHandler.Instance.JoinNetwork(network.NetworkId,
+ APIHandler.Instance.JoinNetwork(this.Dispatcher, network.NetworkId,
allowManaged.IsChecked ?? false,
allowGlobal.IsChecked ?? false,
allowDefault.IsChecked ?? false);
@@ -122,7 +122,7 @@ namespace WinUI
private void AllowGlobal_CheckStateChanged(object sender, RoutedEventArgs e)
{
CheckBox cb = sender as CheckBox;
- APIHandler.Instance.JoinNetwork(network.NetworkId,
+ APIHandler.Instance.JoinNetwork(this.Dispatcher, network.NetworkId,
allowManaged.IsChecked ?? false,
allowGlobal.IsChecked ?? false,
allowDefault.IsChecked ?? false);
@@ -131,7 +131,7 @@ namespace WinUI
private void AllowDefault_CheckStateChanged(object sender, RoutedEventArgs e)
{
CheckBox cb = sender as CheckBox;
- APIHandler.Instance.JoinNetwork(network.NetworkId,
+ APIHandler.Instance.JoinNetwork(this.Dispatcher, network.NetworkId,
allowManaged.IsChecked ?? false,
allowGlobal.IsChecked ?? false,
allowDefault.IsChecked ?? false);
@@ -155,11 +155,11 @@ namespace WinUI
bool managed = allowManaged.IsChecked.Value;
bool defRoute = allowDefault.IsChecked.Value;
- APIHandler.Instance.JoinNetwork(networkId.Text, managed, global, defRoute);
+ APIHandler.Instance.JoinNetwork(this.Dispatcher, networkId.Text, managed, global, defRoute);
}
else
{
- APIHandler.Instance.LeaveNetwork(networkId.Text);
+ APIHandler.Instance.LeaveNetwork(this.Dispatcher, networkId.Text);
}
}
}
diff --git a/windows/WinUI/NetworkMonitor.cs b/windows/WinUI/NetworkMonitor.cs
index c276079d..ce722e45 100644
--- a/windows/WinUI/NetworkMonitor.cs
+++ b/windows/WinUI/NetworkMonitor.cs
@@ -152,10 +152,11 @@ namespace WinUI
Thread.Sleep(2000);
}
}
- catch
+ catch (Exception e)
{
- Console.WriteLine("Monitor Thread Ended");
+ Console.WriteLine("Monitor Thread Exception: " + "\n" + e.StackTrace);
}
+ Console.WriteLine("Monitor Thread Ended");
}
public void SubscribeStatusUpdates(StatusCallback cb)
diff --git a/windows/WinUI/NetworkNameGenerator.cs b/windows/WinUI/NetworkNameGenerator.cs
new file mode 100644
index 00000000..8a75936d
--- /dev/null
+++ b/windows/WinUI/NetworkNameGenerator.cs
@@ -0,0 +1,201 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WinUI
+{
+ class NetworkNameGenerator
+ {
+ public static string GenerateName()
+ {
+ Random r = new Random(DateTime.Now.Millisecond);
+ int firstIndex = r.Next(0, FIRST.Length);
+ int secondIndex = r.Next(0, SECOND.Length);
+ return FIRST[firstIndex] + "_" + SECOND[secondIndex];
+ }
+
+ private static string[] FIRST =
+ {
+ "admiring",
+ "adoring",
+ "agitated",
+ "amazing",
+ "angry",
+ "awesome",
+ "berserk",
+ "big",
+ "clever",
+ "compassionate",
+ "cranky",
+ "crazy",
+ "desperate",
+ "determined",
+ "distracted",
+ "dreamy",
+ "ecstatic",
+ "elated",
+ "elegant",
+ "fervent",
+ "focused",
+ "furious",
+ "gigantic",
+ "gloomy",
+ "goofy",
+ "grave",
+ "happy",
+ "high",
+ "hopeful",
+ "hungry",
+ "insane",
+ "jolly",
+ "jovial",
+ "lonely",
+ "loving",
+ "modest",
+ "nostalgic",
+ "pedantic",
+ "pensive",
+ "prickly",
+ "reverent",
+ "romantic",
+ "sad",
+ "serene",
+ "sharp",
+ "silly",
+ "sleepy",
+ "stoic",
+ "stupefied",
+ "suspicious",
+ "tender",
+ "thirsty",
+ "tiny",
+ "trusting"
+ };
+
+ private static string[] SECOND =
+ {
+ // constructed telephone-like devices in 1854
+ "meucci",
+
+ // prototype make-or-break telephones in 1860
+ "reis",
+
+ // Alexander Graham Bell
+ "bell",
+
+ // designed telephone using water microphone in 1876
+ "gray",
+
+ // Tivadar Puskás invented the telephone switchboard exchange in 1876.
+ "puskas",
+
+ // Thomas Edison, invented the carbon microphone which produced a strong telephone signal.
+ "edison",
+
+ // 1950s, Paul Baran developed the concept Distributed Adaptive Message Block Switching
+ "baran",
+
+ // Donald Davies coined the phrase 'packet switching network'
+ "davies",
+
+ // Robert Licklider helped get ARPANET funded
+ "licklider",
+
+ // Robert Taylor, ARPANET pioneer
+ "taylor",
+
+ // Lawrence Roberts, ARPANET
+ "roberts",
+
+ // Vint Cerf, TCP
+ "cerf",
+
+ // Bob Kahn, TCP
+ "kahn",
+
+ // David P Reed, UDP
+ "reed",
+
+ // Community Memory was created by Efrem Lipkin, Mark Szpakowski, and Lee Felsenstein, acting as The Community Memory Project within the Resource One computer center at Project One in San Francisco.
+ "lipkin",
+ "szpakowski",
+ "felsenstein",
+
+ // The first public dial-up BBS was developed by Ward Christensen and Randy Suess.
+ "christensen",
+ "suess",
+
+ // Joybubbles (May 25, 1949 – August 8, 2007), born Josef Carl Engressia, Jr. in Richmond, Virginia, USA, was an early phone phreak.
+ "engressia",
+ "joybubbles",
+
+ // John Thomas Draper (born 1943), also known as Captain Crunch, Crunch or Crunchman (after Cap'n Crunch breakfast cereal mascot), is an American computer programmer and former phone phreak
+ "draper",
+
+ // Dennis C. Hayes, founder of Hayes Microcomputer Products
+ // "The Modem of Dennis Hayes and Dale Heatherington."
+ "hayes",
+ "heatherington",
+
+ // "Ethernet was developed at Xerox PARC between 1973 and 1974.[7][8] It was inspired by ALOHAnet, which Robert Metcalfe had studied as part of his PhD dissertation."
+ "metcalfe",
+
+ // William Bradford Shockley Jr. (February 13, 1910 – August 12, 1989) was an American physicist and inventor. Shockley was the manager of a research group that included John Bardeen and Walter Brattain. The three scientists invented the point contact transistor in 1947
+ "shockley",
+ "bardeen",
+ "brattain",
+
+ // "Randall Erck invented the modern modem as we know it today. There were devices similar to modems used by the military, but they were designed more for the purpose of sending encripted nuclear launch codes to various bases around the world."
+ "erck",
+
+ // Leonard Kleinrock, packet switching network pioneer
+ "kleinrock",
+
+ // Tim Berners-Lee, WWW
+ "berners_lee",
+
+ // Steve Wozniak, early phone phreak
+ "wozniak",
+
+ // James Fields Smathers of Kansas City invented what is considered the first practical power-operated typewriter in 1914.
+ "smathers",
+
+ // The teleprinter evolved through a series of inventions by a number of engineers, including Royal Earl House, David Edward Hughes, Emile Baudot, Donald Murray, Charles L. Krum, Edward Kleinschmidt and Frederick G. Creed.
+ "house",
+ "hughes",
+ "baudot",
+ "murray",
+ "krum",
+ "kleinschmidt",
+ "creed",
+
+ // Ron Rosenbaum, author of "Secrets of the Little Blue Box" which mainstreamed phone phreaking
+ "rosenbaum",
+
+ // Bram Cohen. Bram Cohen (born October 12, 1975) is an American computer programmer, best known as the author of the peer-to-peer (P2P) BitTorrent protocol,
+ "cohen",
+
+ // Jarkko Oikarinen (born 16 August 1967, in Kuusamo, Finland) is the inventor of the first Internet chat network, called Internet Relay Chat (IRC), where he is known as WiZ.
+ "oikarinen",
+
+ // "What you probably didn't know is that the author of Trumpet Winsock — Peter Tattam from Tasmania, Australia — didn't see much money for his efforts."
+ "tattam",
+
+ // Satoshi Nakamoto
+ "nakamoto",
+
+ // Philo Farnsworth, inventor of the first practical TV tube
+ "farnsworth",
+
+ // Scottish inventor John Logie Baird employed the Nipkow disk in his prototype video systems. On 25 March 1925, Baird gave the first public demonstration of televised silhouette images in motion, at Selfridge's Department Store in London.
+ "baird",
+
+ // Beginning in 1836, the American artist Samuel F. B. Morse, the American physicist Joseph Henry, and Alfred Vail developed an electrical telegraph system.
+ "morse",
+ "henry",
+ "vail"
+ };
+ }
+}
diff --git a/windows/WinUI/OnboardProcess/CreateAccount.xaml b/windows/WinUI/OnboardProcess/CreateAccount.xaml
new file mode 100644
index 00000000..ddf50252
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/CreateAccount.xaml
@@ -0,0 +1,38 @@
+<UserControl x:Class="WinUI.OnboardProcess.CreateAccount"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:local="clr-namespace:WinUI.OnboardProcess"
+ mc:Ignorable="d"
+ d:DesignHeight="300" d:DesignWidth="300">
+ <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="*"/>
+ <RowDefinition Height="Auto"/>
+ </Grid.RowDefinitions>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="Auto"/>
+ <ColumnDefinition Width="Auto"/>
+ </Grid.ColumnDefinitions>
+
+ <Label Grid.Column="0" Grid.Row="0" HorizontalAlignment="Right" Margin="10">Email Address:</Label>
+ <TextBox x:Name="EmailAddressTextBox" Grid.Column="1" Grid.Row="0" Width="150" Margin="10"></TextBox>
+
+ <Label Grid.Column="0" Grid.Row="1" HorizontalAlignment="Right" Margin="10">Password:</Label>
+ <PasswordBox x:Name="PasswordTextBox1" Grid.Column="1" Grid.Row="1" PasswordChar="*" Margin="10"/>
+ <Label Grid.Column="0" Grid.Row="2" HorizontalAlignment="Right" Margin="10">Repeat Password:</Label>
+ <PasswordBox x:Name="PasswordTextBox2" Grid.Column="1" Grid.Row="2" PasswordChar="*" Margin="10"/>
+
+ <Button Grid.Column="1" Grid.Row="3" Click="CreateAccount_Click" Margin="10" Content="Create Account" Background="#FFFFB354" Width="90" HorizontalAlignment="Right"/>
+
+ <Label x:Name="ErrorText" Grid.Row="4" Grid.ColumnSpan="2" HorizontalAlignment="Center"></Label>
+ <Label Grid.Row="5"></Label>
+ <Button Grid.Column="0" Grid.Row="6" Background="#FFFFB354" Click="BackButton_Click">Go Back</Button>
+ </Grid>
+</UserControl>
diff --git a/windows/WinUI/OnboardProcess/CreateAccount.xaml.cs b/windows/WinUI/OnboardProcess/CreateAccount.xaml.cs
new file mode 100644
index 00000000..72ba2182
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/CreateAccount.xaml.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace WinUI.OnboardProcess
+{
+ /// <summary>
+ /// Interaction logic for CreateAccount.xaml
+ /// </summary>
+ public partial class CreateAccount : UserControl, ISwitchable
+ {
+ public CreateAccount()
+ {
+ InitializeComponent();
+ }
+
+ public void UtilizeState(object state)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void CreateAccount_Click(object sender, RoutedEventArgs e)
+ {
+ DoCreateAccount();
+ }
+
+ public void BackButton_Click(object sender, RoutedEventArgs e)
+ {
+ Switcher.Switch(new RegisterOrLogIn());
+ }
+
+ public async void DoCreateAccount()
+ {
+ if (PasswordTextBox1.Password.ToString() != PasswordTextBox2.Password.ToString())
+ {
+ ErrorText.Content = "Passwords do not match!";
+ }
+ else
+ {
+ CentralAPI api = CentralAPI.Instance;
+ bool accountCreated = await api.Login(EmailAddressTextBox.Text,
+ PasswordTextBox1.Password.ToString(), true);
+
+ if (accountCreated)
+ {
+ Switcher.Switch(new CreateOrJoin());
+ }
+ else
+ {
+ ErrorText.Content = "An error ocurred while creating your account.";
+ }
+ }
+ }
+ }
+}
diff --git a/windows/WinUI/OnboardProcess/CreateOrJoin.xaml b/windows/WinUI/OnboardProcess/CreateOrJoin.xaml
new file mode 100644
index 00000000..21413e84
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/CreateOrJoin.xaml
@@ -0,0 +1,49 @@
+<UserControl x:Class="WinUI.OnboardProcess.CreateOrJoin"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:local="clr-namespace:WinUI.OnboardProcess"
+ mc:Ignorable="d"
+ d:DesignHeight="300" d:DesignWidth="400">
+ <Grid HorizontalAlignment="Stretch" Margin="15">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="*"/>
+ <RowDefinition Height="Auto"/>
+ </Grid.RowDefinitions>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*"/>
+ <ColumnDefinition Width="Auto"/>
+ <ColumnDefinition Width="*"/>
+ </Grid.ColumnDefinitions>
+
+ <Button Grid.Column="1" x:Name="CreateButton" Content="Create a Network" Background="#FFFFB354" Click="OnCreateButtonClick"/>
+
+ <Label Grid.Column="1" Grid.Row="1" Content="Or" HorizontalAlignment="Center"/>
+
+ <Label Grid.Column="1" Grid.Row="2" Content="Join a Network:" HorizontalAlignment="Center"/>
+
+ <ListBox Grid.ColumnSpan="3" Grid.Column="0" Grid.Row="3" Name="listViewDataBinding" HorizontalContentAlignment="Stretch">
+ <ListBox.ItemTemplate>
+ <DataTemplate>
+ <Grid HorizontalAlignment="Stretch">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="Auto"/>
+ <ColumnDefinition Width="*"/>
+ <ColumnDefinition Width="Auto"/>
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto"/>
+ </Grid.RowDefinitions>
+ <TextBlock Grid.Column="0" Grid.Row="0" Margin="5" HorizontalAlignment="Left" Text="{Binding Id}"/>
+ <TextBlock Grid.Column="1" Grid.Row="0" Margin="5" HorizontalAlignment="Center" Text="{Binding Config.Name}"/>
+ <Button Grid.Column="2" Grid.Row="0" Margin="5" Content="Join" HorizontalAlignment="Right" Background="#FFFFB354" Tag="{Binding Id}" Click="OnJoinButtonClick"/>
+ </Grid>
+ </DataTemplate>
+ </ListBox.ItemTemplate>
+ </ListBox>
+ </Grid>
+</UserControl>
diff --git a/windows/WinUI/OnboardProcess/CreateOrJoin.xaml.cs b/windows/WinUI/OnboardProcess/CreateOrJoin.xaml.cs
new file mode 100644
index 00000000..91330540
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/CreateOrJoin.xaml.cs
@@ -0,0 +1,98 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace WinUI.OnboardProcess
+{
+ /// <summary>
+ /// Interaction logic for CreateOrJoin.xaml
+ /// </summary>
+ public partial class CreateOrJoin : UserControl, ISwitchable
+ {
+ private List<CentralNetwork> networkList = new List<CentralNetwork>();
+
+ public CreateOrJoin()
+ {
+ InitializeComponent();
+ listViewDataBinding.ItemsSource = networkList;
+
+ GetAvailableNetworks();
+ }
+
+ public void UtilizeState(object state)
+ {
+ throw new NotImplementedException();
+ }
+
+ private async void GetAvailableNetworks()
+ {
+ CentralAPI api = CentralAPI.Instance;
+
+ List<CentralNetwork> networks = await api.GetNetworkList();
+
+ foreach (CentralNetwork n in networks)
+ {
+ networkList.Add(n);
+ }
+
+ listViewDataBinding.Items.Refresh();
+ }
+
+ public void OnJoinButtonClick(object sender, RoutedEventArgs e)
+ {
+ Button button = sender as Button;
+ string networkId = button.Tag as string;
+
+ APIHandler handler = APIHandler.Instance;
+
+ handler.JoinNetwork(this.Dispatcher, networkId);
+
+ AuthorizeNetworkMember(networkId);
+ }
+
+ public void OnCreateButtonClick(object sender, RoutedEventArgs e)
+ {
+ CreateNewNetwork();
+ }
+
+ private async void CreateNewNetwork()
+ {
+ CentralAPI api = CentralAPI.Instance;
+
+ CentralNetwork newNetwork = await api.CreateNewNetwork();
+
+ APIHandler handler = APIHandler.Instance;
+
+ handler.JoinNetwork(this.Dispatcher, newNetwork.Id);
+
+ AuthorizeNetworkMember(newNetwork.Id);
+ }
+
+ private async void AuthorizeNetworkMember(string networkId)
+ {
+ string nodeId = APIHandler.Instance.NodeAddress();
+
+ bool authorized = await CentralAPI.Instance.AuthorizeNode(nodeId, networkId);
+
+ if (authorized)
+ {
+ Switcher.Switch(new Finished());
+ }
+ else
+ {
+
+ }
+ }
+ }
+}
diff --git a/windows/WinUI/OnboardProcess/EnterToken.xaml b/windows/WinUI/OnboardProcess/EnterToken.xaml
new file mode 100644
index 00000000..1880167d
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/EnterToken.xaml
@@ -0,0 +1,29 @@
+<UserControl x:Class="WinUI.OnboardProcess.EnterToken"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:local="clr-namespace:WinUI.OnboardProcess"
+ mc:Ignorable="d"
+ d:DesignHeight="300" d:DesignWidth="300">
+ <Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="10">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="*"/>
+ <RowDefinition Height="Auto"/>
+ </Grid.RowDefinitions>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="Auto"/>
+ <ColumnDefinition Width="*"/>
+ </Grid.ColumnDefinitions>
+ <TextBlock Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Margin="10" TextWrapping="Wrap" HorizontalAlignment="Center" Text="Your API Token can be found or created on https://my.zerotier.com"/>
+ <TextBlock Grid.Column="0" Grid.Row="1" Text="API Token:" Margin="10"/>
+ <TextBox x:Name="APITokenInput" Grid.Column="1" Grid.Row="1" Margin="10"/>
+
+ <Button Grid.Column="1" Grid.Row="2" Background="#FFFFB354" Content="Next" HorizontalAlignment="Right" Click="Next_Click" Margin="10"/>
+ <Label Grid.Row="3"/>
+ <Button Grid.Column="0" Grid.Row="4" Background="#FFFFB354" Content="Go Back" Click="BackButton_Click" />
+ </Grid>
+</UserControl>
diff --git a/windows/WinUI/OnboardProcess/EnterToken.xaml.cs b/windows/WinUI/OnboardProcess/EnterToken.xaml.cs
new file mode 100644
index 00000000..5d2cc515
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/EnterToken.xaml.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace WinUI.OnboardProcess
+{
+ /// <summary>
+ /// Interaction logic for EnterToken.xaml
+ /// </summary>
+ public partial class EnterToken : UserControl, ISwitchable
+ {
+ public EnterToken()
+ {
+ InitializeComponent();
+
+ if (!string.IsNullOrEmpty(CentralAPI.Instance.Central.APIKey))
+ {
+ APITokenInput.Text = CentralAPI.Instance.Central.APIKey;
+ }
+ }
+
+ public void UtilizeState(object staqte)
+ {
+
+ }
+
+ private void Next_Click(object sender, RoutedEventArgs e)
+ {
+ CentralAPI api = CentralAPI.Instance;
+
+ if (api.Central.APIKey != APITokenInput.Text)
+ {
+ CentralServer server = new CentralServer();
+ server.APIKey = APITokenInput.Text;
+ api.Central = server;
+ }
+
+ Switcher.Switch(new CreateOrJoin());
+ }
+
+ private void BackButton_Click(object sender, RoutedEventArgs e)
+ {
+ Switcher.Switch(new RegisterOrLogIn());
+ }
+ }
+}
diff --git a/windows/WinUI/OnboardProcess/Finished.xaml b/windows/WinUI/OnboardProcess/Finished.xaml
new file mode 100644
index 00000000..d29f536c
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/Finished.xaml
@@ -0,0 +1,27 @@
+<UserControl x:Class="WinUI.OnboardProcess.Finished"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:local="clr-namespace:WinUI.OnboardProcess"
+ mc:Ignorable="d"
+ d:DesignHeight="300" d:DesignWidth="300">
+ <Grid VerticalAlignment="Center" HorizontalAlignment="Stretch" Margin="10">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ </Grid.RowDefinitions>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="Auto" />
+ </Grid.ColumnDefinitions>
+
+ <TextBlock Grid.Column="0" Grid.Row="0" Text="All Finished!" HorizontalAlignment="Center"/>
+ <TextBlock Grid.Column="0" Grid.Row="1" HorizontalAlignment="Center" Text=""/>
+ <TextBlock Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="2" MaxWidth="270" HorizontalAlignment="Center" Margin="10" Text="You've now joined your first ZeroTier network. Now go to add your other machines!" TextWrapping="Wrap"/>
+ <TextBlock Grid.Column="0" Grid.Row="3" HorizontalAlignment="Center" Text=""/>
+ <Button Grid.Column="0" Grid.Row="4" MaxWidth="100" Click="DoneButton_Click" Background="#FFFFB354" Margin="10">Done!</Button>
+ </Grid>
+</UserControl>
diff --git a/windows/WinUI/OnboardProcess/Finished.xaml.cs b/windows/WinUI/OnboardProcess/Finished.xaml.cs
new file mode 100644
index 00000000..a34abfe2
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/Finished.xaml.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace WinUI.OnboardProcess
+{
+ /// <summary>
+ /// Interaction logic for Finished.xaml
+ /// </summary>
+ public partial class Finished : UserControl, ISwitchable
+ {
+ public Finished()
+ {
+ InitializeComponent();
+ }
+ public void UtilizeState(object state)
+ {
+ throw new NotImplementedException();
+ }
+
+ private void DoneButton_Click(object sender, RoutedEventArgs e)
+ {
+ Window.GetWindow(this).Close();
+ }
+ }
+}
diff --git a/windows/WinUI/OnboardProcess/LogIn.xaml b/windows/WinUI/OnboardProcess/LogIn.xaml
new file mode 100644
index 00000000..501f7e0f
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/LogIn.xaml
@@ -0,0 +1,35 @@
+<UserControl x:Class="WinUI.OnboardProcess.LogIn"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:local="clr-namespace:WinUI.OnboardProcess"
+ mc:Ignorable="d"
+ d:DesignHeight="300" d:DesignWidth="300">
+ <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="*" />
+ <RowDefinition Height="Auto"/>
+ </Grid.RowDefinitions>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="Auto"/>
+ <ColumnDefinition Width="Auto"/>
+ </Grid.ColumnDefinitions>
+
+ <Label Grid.Column="0" Grid.Row="0" HorizontalAlignment="Right" Margin="10">Email Address:</Label>
+ <TextBox x:Name="EmailAddressTextBox" Grid.Column="1" Grid.Row="0" MinWidth="150" Margin="10"></TextBox>
+
+ <Label Grid.Column="0" Grid.Row="1" HorizontalAlignment="Right" Margin="10">Password:</Label>
+ <PasswordBox x:Name="PasswordTextBox" Grid.Column="1" Grid.Row="1" PasswordChar="*" Margin="10"/>
+
+ <Button Grid.Column="1" Grid.Row="2" Click="LoginButton_Click" Content="LogIn" Background="#FFFFB354" Width="90" Margin="10" HorizontalAlignment="Right"/>
+
+ <Label x:Name="ErrorText" Grid.Row="3" Grid.ColumnSpan="2" HorizontalAlignment="Center"></Label>
+ <Label Grid.Row="4"></Label>
+ <Button Grid.Column="0" Grid.Row="5" Background="#FFFFB354" Click="BackButton_Click">Go Back</Button>
+ </Grid>
+</UserControl>
diff --git a/windows/WinUI/OnboardProcess/LogIn.xaml.cs b/windows/WinUI/OnboardProcess/LogIn.xaml.cs
new file mode 100644
index 00000000..8657b800
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/LogIn.xaml.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace WinUI.OnboardProcess
+{
+ /// <summary>
+ /// Interaction logic for LogIn.xaml
+ /// </summary>
+ public partial class LogIn : UserControl, ISwitchable
+ {
+ public LogIn()
+ {
+ InitializeComponent();
+ }
+
+ public void UtilizeState(object state)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void LoginButton_Click(object sender, RoutedEventArgs e)
+ {
+ DoLogin();
+ }
+
+ public void BackButton_Click(object sender, RoutedEventArgs e)
+ {
+ Switcher.Switch(new RegisterOrLogIn());
+ }
+
+ private async void DoLogin()
+ {
+ CentralAPI api = CentralAPI.Instance;
+ bool didLogIn = await api.Login(EmailAddressTextBox.Text, PasswordTextBox.Password.ToString(), false);
+ if (didLogIn)
+ {
+ Switcher.Switch(new CreateOrJoin());
+ }
+ else
+ {
+ ErrorText.Content = "Invalid username or password";
+ }
+ }
+ }
+}
diff --git a/windows/WinUI/OnboardProcess/RegisterOrLogIn.xaml b/windows/WinUI/OnboardProcess/RegisterOrLogIn.xaml
new file mode 100644
index 00000000..01f3ba9d
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/RegisterOrLogIn.xaml
@@ -0,0 +1,27 @@
+<UserControl x:Class="WinUI.OnboardProcess.RegisterOrLogIn"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:local="clr-namespace:WinUI.OnboardProcess"
+ mc:Ignorable="d"
+ d:DesignHeight="300" d:DesignWidth="300">
+ <StackPanel>
+ <TextBlock HorizontalAlignment="Center" FontSize="20" FontWeight="Bold" FontFamily="Segoe UI">Welcome to ZeroTier</TextBlock>
+ <TextBlock HorizontalAlignment="Center" FontSize="16">Let's get started!</TextBlock>
+ <TextBlock HorizontalAlignment="Center"> </TextBlock>
+ <TextBlock HorizontalAlignment="Center" TextWrapping="Wrap">If you haven't yet created an account, click "Create Account" below. If you have an account, you can log in with your username/password, or simply enter an API key.</TextBlock>
+ <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
+ <StackPanel.Resources>
+ <Style TargetType="{x:Type Button}">
+ <Setter Property="Margin" Value="10,10,0,0"/>
+ </Style>
+ </StackPanel.Resources>
+ <Button x:Name="CreateAccountButton" Margin="10,5" Content="Create Account" Background="#FFFFB354" Click="CreateAccountButton_Click"/>
+
+ <Button x:Name="LogInButton" Margin="10,5" Content="Log In" Background="#FFFFB354" Click="LogInButton_Click"/>
+
+ <Button x:Name="TokenButton" Margin="10,5" Content="API Token" Background="#FFFFB354" Click="APIToken_Click"/>
+ </StackPanel>
+ </StackPanel>
+</UserControl>
diff --git a/windows/WinUI/OnboardProcess/RegisterOrLogIn.xaml.cs b/windows/WinUI/OnboardProcess/RegisterOrLogIn.xaml.cs
new file mode 100644
index 00000000..19c578dd
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/RegisterOrLogIn.xaml.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace WinUI.OnboardProcess
+{
+ /// <summary>
+ /// Interaction logic for RegisterOrLogIn.xaml
+ /// </summary>
+ public partial class RegisterOrLogIn : UserControl, ISwitchable
+ {
+ public RegisterOrLogIn()
+ {
+ InitializeComponent();
+ }
+
+ public void UtilizeState(object state)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void CreateAccountButton_Click(object sender, System.Windows.RoutedEventArgs e)
+ {
+ Switcher.Switch(new CreateAccount());
+ }
+
+ private void LogInButton_Click(object sender, RoutedEventArgs e)
+ {
+ Switcher.Switch(new LogIn());
+ }
+
+ public void APIToken_Click(object sender, RoutedEventArgs e)
+ {
+ Switcher.Switch(new EnterToken());
+ }
+ }
+}
diff --git a/windows/WinUI/PageSwitcher.xaml b/windows/WinUI/PageSwitcher.xaml
new file mode 100644
index 00000000..2e463e7a
--- /dev/null
+++ b/windows/WinUI/PageSwitcher.xaml
@@ -0,0 +1,13 @@
+<Window x:Class="WinUI.PageSwitcher"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:local="clr-namespace:WinUI"
+ mc:Ignorable="d"
+ Icon="ZeroTierIcon.ico"
+ Title="ZeroTier One" Height="300" Width="400">
+ <Grid>
+
+ </Grid>
+</Window>
diff --git a/windows/WinUI/PageSwitcher.xaml.cs b/windows/WinUI/PageSwitcher.xaml.cs
new file mode 100644
index 00000000..c9aa659f
--- /dev/null
+++ b/windows/WinUI/PageSwitcher.xaml.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+
+namespace WinUI
+{
+ /// <summary>
+ /// Interaction logic for PageSwitcher.xaml
+ /// </summary>
+ public partial class PageSwitcher : Window
+ {
+ public PageSwitcher()
+ {
+ InitializeComponent();
+ Switcher.pageSwitcher = this;
+
+ CentralAPI api = CentralAPI.Instance;
+
+ if (api.HasAccessToken())
+ {
+ Switcher.Switch(new OnboardProcess.CreateOrJoin());
+ }
+ else
+ {
+ Switcher.Switch(new OnboardProcess.RegisterOrLogIn());
+ }
+ }
+
+ public void Navigate(UserControl nextPage)
+ {
+ this.Content = nextPage;
+ }
+
+ public void Navigate(UserControl nextPage, object state)
+ {
+ this.Content = nextPage;
+ ISwitchable s = nextPage as ISwitchable;
+
+ if (s != null)
+ s.UtilizeState(state);
+ else
+ throw new ArgumentException("NextPage is not ISwitchable! "
+ + nextPage.Name.ToString());
+ }
+ }
+}
diff --git a/windows/WinUI/PreferencesView.xaml b/windows/WinUI/PreferencesView.xaml
index ac61fff1..fd170493 100644
--- a/windows/WinUI/PreferencesView.xaml
+++ b/windows/WinUI/PreferencesView.xaml
@@ -6,8 +6,25 @@
xmlns:local="clr-namespace:WinUI"
mc:Ignorable="d"
Title="PreferencesView" SizeToContent="WidthAndHeight" Height="Auto" Width="Auto" Icon="ZeroTierIcon.ico">
- <Grid>
- <CheckBox x:Name="startupCheckbox" Content="Launch ZeroTier On Startup" HorizontalAlignment="Left" Margin="10,10,10,10" VerticalAlignment="Top" Checked="startupCheckbox_Checked" Unchecked="startupCheckbox_Unchecked"/>
+ <Grid>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="Auto"/>
+ <ColumnDefinition Width="Auto"/>
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ </Grid.RowDefinitions>
+ <CheckBox x:Name="startupCheckbox" Content="Launch ZeroTier On Startup" HorizontalAlignment="Left" Margin="10" VerticalAlignment="Top" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0"/>
+
+ <TextBlock Text="Central Instance:" Grid.Row="1" Grid.Column="0" Margin="10"/>
+ <TextBox x:Name="CentralInstanceTextBox" Grid.Row="1" Grid.Column="1" MinWidth="200" Margin="10"/>
- </Grid>
+ <TextBlock Text="API Key:" Grid.Row="2" Grid.Column="0" Margin="10"/>
+ <TextBox x:Name="APIKeyTextBox" Grid.Row="2" Grid.Column="1" MinWidth="200" Margin="10"/>
+
+ <Button x:Name="OKButton" Grid.Row="3" Grid.Column="1" Background="#FFFFB354" Content="OK" Margin="10" Width="90" HorizontalAlignment="Right" Click="OKButton_Clicked"/>
+ </Grid>
</Window>
diff --git a/windows/WinUI/PreferencesView.xaml.cs b/windows/WinUI/PreferencesView.xaml.cs
index c6733be6..1a5ed756 100644
--- a/windows/WinUI/PreferencesView.xaml.cs
+++ b/windows/WinUI/PreferencesView.xaml.cs
@@ -22,7 +22,7 @@ namespace WinUI
{
public static string AppName = "ZeroTier One";
private RegistryKey rk = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);
-
+ private string AppLocation = System.Reflection.Assembly.GetExecutingAssembly().Location;
public PreferencesView()
{
InitializeComponent();
@@ -30,21 +30,45 @@ namespace WinUI
string keyValue = rk.GetValue(AppName) as string;
- if (keyValue != null && keyValue.Equals(System.Reflection.Assembly.GetExecutingAssembly().Location))
+ if (keyValue != null && keyValue.Equals(AppLocation))
{
startupCheckbox.IsChecked = true;
}
- }
- private void startupCheckbox_Checked(object sender, RoutedEventArgs e)
- {
- rk.SetValue(AppName, System.Reflection.Assembly.GetExecutingAssembly().Location);
+ CentralAPI api = CentralAPI.Instance;
+ CentralInstanceTextBox.Text = api.Central.ServerURL;
+ APIKeyTextBox.Text = api.Central.APIKey;
}
- private void startupCheckbox_Unchecked(object sender, RoutedEventArgs e)
+ private void OKButton_Clicked(object sender, RoutedEventArgs e)
{
- rk.DeleteValue(AppName);
- }
+ CentralAPI api = CentralAPI.Instance;
+
+ if (api.Central.ServerURL != CentralInstanceTextBox.Text ||
+ api.Central.APIKey != APIKeyTextBox.Text)
+ {
+ CentralServer newServer = new CentralServer();
+ newServer.ServerURL = CentralInstanceTextBox.Text;
+ newServer.APIKey = APIKeyTextBox.Text;
+
+ api.Central = newServer;
+ }
+ if (startupCheckbox.IsChecked.HasValue && (bool)startupCheckbox.IsChecked)
+ {
+ rk.SetValue(AppName, AppLocation);
+ }
+ else
+ {
+ string keyValue = rk.GetValue(AppName) as string;
+
+ if (keyValue != null && keyValue.Equals(AppLocation))
+ {
+ rk.DeleteValue(AppName);
+ }
+ }
+
+ Close();
+ }
}
}
diff --git a/windows/WinUI/Switcher.cs b/windows/WinUI/Switcher.cs
new file mode 100644
index 00000000..f48835df
--- /dev/null
+++ b/windows/WinUI/Switcher.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows.Controls;
+using System.Threading.Tasks;
+
+namespace WinUI
+{
+ public static class Switcher
+ {
+ public static PageSwitcher pageSwitcher;
+
+ public static void Switch(UserControl newPage)
+ {
+ pageSwitcher.Navigate(newPage);
+ }
+
+ public static void Switch(UserControl newPage, object state)
+ {
+ pageSwitcher.Navigate(newPage, state);
+ }
+ }
+}
diff --git a/windows/WinUI/Themes/Generic.xaml b/windows/WinUI/Themes/Generic.xaml
index 2b47588d..689f0740 100644
--- a/windows/WinUI/Themes/Generic.xaml
+++ b/windows/WinUI/Themes/Generic.xaml
@@ -3,5 +3,4 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WinUI">
-
</ResourceDictionary>
diff --git a/windows/WinUI/ToolbarItem.xaml b/windows/WinUI/ToolbarItem.xaml
index 3b064fc4..85e4122a 100644
--- a/windows/WinUI/ToolbarItem.xaml
+++ b/windows/WinUI/ToolbarItem.xaml
@@ -41,6 +41,9 @@
</CollectionContainer>
<Separator/>
+ <MenuItem Header="ZeroTier Central"
+ Click="ToolbarItem_CentralClicked"/>
+
<MenuItem Header="About..."
Click="ToolbarItem_AboutClicked"/>
<MenuItem Header="Preferences..."
diff --git a/windows/WinUI/ToolbarItem.xaml.cs b/windows/WinUI/ToolbarItem.xaml.cs
index 29791a4e..c16de3c3 100644
--- a/windows/WinUI/ToolbarItem.xaml.cs
+++ b/windows/WinUI/ToolbarItem.xaml.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
+using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
@@ -44,6 +45,11 @@ namespace WinUI
private ObservableCollection<MenuItem> _networkCollection = new ObservableCollection<MenuItem>();
+ private static Boolean shouldShowOnboardProcess = true;
+#if DEBUG
+ private static bool isFirstRun = true;
+#endif
+
public ObservableCollection<MenuItem> NetworkCollection
{
get { return _networkCollection; }
@@ -79,13 +85,30 @@ namespace WinUI
{
if (networks != null)
{
- this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ if (networks.Count > 0)
+ {
+#if DEBUG
+ if (isFirstRun)
+ {
+ shouldShowOnboardProcess = true;
+ isFirstRun = false;
+ }
+ else
+ {
+ shouldShowOnboardProcess = false;
+ }
+#else
+ shouldShowOnboardProcess = false;
+#endif
+ }
+
+ Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
NetworkCollection.Clear();
foreach (ZeroTierNetwork n in networks)
{
MenuItem item = new MenuItem();
- item.Header = n.Title;
+ item.Header = n.Title.Replace("_", "__");
item.DataContext = n;
item.IsChecked = n.IsConnected;
item.Click += ToolbarItem_NetworkClicked;
@@ -93,6 +116,18 @@ namespace WinUI
NetworkCollection.Add(item);
}
}));
+
+ if (shouldShowOnboardProcess)
+ {
+ // TODO: Show onboarding process window (on main thread
+ Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ PageSwitcher ps = new PageSwitcher();
+ ps.Show();
+ }));
+
+ shouldShowOnboardProcess = false;
+ }
}
}
@@ -111,7 +146,19 @@ namespace WinUI
private void ToolbarItem_NodeIDClicked(object sender, System.Windows.RoutedEventArgs e)
{
- Clipboard.SetText(nodeId);
+ try
+ {
+ Clipboard.SetDataObject(nodeId);
+ }
+ catch (ArgumentNullException)
+ {
+ // tried to copy a null nodeId
+ Console.WriteLine("ArgumentNullException");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(ex.ToString());
+ }
}
private void ToolbarItem_ShowNetworksClicked(object sender, System.Windows.RoutedEventArgs e)
@@ -182,6 +229,11 @@ namespace WinUI
joinNetView = null;
}
+ private void ToolbarItem_CentralClicked(object sender, System.EventArgs e)
+ {
+ Process.Start("https://my.zerotier.com");
+ }
+
private void ToolbarItem_AboutClicked(object sender, System.EventArgs e)
{
if (aboutView == null)
@@ -255,7 +307,7 @@ namespace WinUI
private void ToolbarItem_QuitClicked(object sender, System.EventArgs e)
{
NetworkMonitor.Instance.StopMonitor();
- this.Close();
+ Close();
Application.Current.Shutdown();
}
@@ -269,11 +321,11 @@ namespace WinUI
ZeroTierNetwork network = item.DataContext as ZeroTierNetwork;
if (item.IsChecked)
{
- APIHandler.Instance.LeaveNetwork(network.NetworkId);
+ APIHandler.Instance.LeaveNetwork(Dispatcher, network.NetworkId);
}
else
{
- APIHandler.Instance.JoinNetwork(network.NetworkId, network.AllowManaged, network.AllowGlobal, network.AllowDefault);
+ APIHandler.Instance.JoinNetwork(Dispatcher, network.NetworkId, network.AllowManaged, network.AllowGlobal, network.AllowDefault);
}
}
}
diff --git a/windows/WinUI/WinUI.csproj b/windows/WinUI/WinUI.csproj
index 6b8013e0..6bbc1cd7 100644
--- a/windows/WinUI/WinUI.csproj
+++ b/windows/WinUI/WinUI.csproj
@@ -80,6 +80,7 @@
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
+ <Reference Include="System.Net.Http" />
<Reference Include="System.Printing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
@@ -104,20 +105,50 @@
<Compile Include="AboutView.xaml.cs">
<DependentUpon>AboutView.xaml</DependentUpon>
</Compile>
+ <Compile Include="CentralAPI.cs" />
+ <Compile Include="CentralLogin.cs" />
+ <Compile Include="CentralNetwork.cs" />
+ <Compile Include="CentralServer.cs" />
+ <Compile Include="CentralToken.cs" />
+ <Compile Include="CentralUser.cs" />
+ <Compile Include="ISwitchable.cs" />
<Compile Include="JoinNetworkView.xaml.cs">
<DependentUpon>JoinNetworkView.xaml</DependentUpon>
</Compile>
<Compile Include="NetworkMonitor.cs" />
+ <Compile Include="NetworkNameGenerator.cs" />
<Compile Include="NetworkRoute.cs" />
<Compile Include="NetworksPage.xaml.cs">
<DependentUpon>NetworksPage.xaml</DependentUpon>
</Compile>
+ <Compile Include="OnboardProcess\CreateAccount.xaml.cs">
+ <DependentUpon>CreateAccount.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="OnboardProcess\CreateOrJoin.xaml.cs">
+ <DependentUpon>CreateOrJoin.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="OnboardProcess\EnterToken.xaml.cs">
+ <DependentUpon>EnterToken.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="OnboardProcess\Finished.xaml.cs">
+ <DependentUpon>Finished.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="OnboardProcess\LogIn.xaml.cs">
+ <DependentUpon>LogIn.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="OnboardProcess\RegisterOrLogIn.xaml.cs">
+ <DependentUpon>RegisterOrLogIn.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="PageSwitcher.xaml.cs">
+ <DependentUpon>PageSwitcher.xaml</DependentUpon>
+ </Compile>
<Compile Include="PeersPage.xaml.cs">
<DependentUpon>PeersPage.xaml</DependentUpon>
</Compile>
<Compile Include="PreferencesView.xaml.cs">
<DependentUpon>PreferencesView.xaml</DependentUpon>
</Compile>
+ <Compile Include="Switcher.cs" />
<Compile Include="ToolbarItem.xaml.cs">
<DependentUpon>ToolbarItem.xaml</DependentUpon>
</Compile>
@@ -154,6 +185,34 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
+ <Page Include="OnboardProcess\CreateAccount.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
+ <Page Include="OnboardProcess\CreateOrJoin.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
+ <Page Include="OnboardProcess\EnterToken.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
+ <Page Include="OnboardProcess\Finished.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
+ <Page Include="OnboardProcess\LogIn.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
+ <Page Include="OnboardProcess\RegisterOrLogIn.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
+ <Page Include="PageSwitcher.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
<Page Include="PeersPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
diff --git a/windows/ZeroTierOne/ZeroTierOne.vcxproj b/windows/ZeroTierOne/ZeroTierOne.vcxproj
index 9715f3d9..105ea127 100644
--- a/windows/ZeroTierOne/ZeroTierOne.vcxproj
+++ b/windows/ZeroTierOne/ZeroTierOne.vcxproj
@@ -1,411 +1,409 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <ItemGroup Label="ProjectConfigurations">
- <ProjectConfiguration Include="Debug|Win32">
- <Configuration>Debug</Configuration>
- <Platform>Win32</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Debug|x64">
- <Configuration>Debug</Configuration>
- <Platform>x64</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Profile|Win32">
- <Configuration>Profile</Configuration>
- <Platform>Win32</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Profile|x64">
- <Configuration>Profile</Configuration>
- <Platform>x64</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Release|Win32">
- <Configuration>Release</Configuration>
- <Platform>Win32</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Release|x64">
- <Configuration>Release</Configuration>
- <Platform>x64</Platform>
- </ProjectConfiguration>
- </ItemGroup>
- <ItemGroup>
- <ClCompile Include="..\..\controller\EmbeddedNetworkController.cpp" />
- <ClCompile Include="..\..\controller\JSONDB.cpp" />
- <ClCompile Include="..\..\ext\http-parser\http_parser.c" />
- <ClCompile Include="..\..\ext\libnatpmp\getgateway.c" />
- <ClCompile Include="..\..\ext\libnatpmp\natpmp.c" />
- <ClCompile Include="..\..\ext\libnatpmp\wingettimeofday.c" />
- <ClCompile Include="..\..\ext\miniupnpc\connecthostport.c" />
- <ClCompile Include="..\..\ext\miniupnpc\igd_desc_parse.c" />
- <ClCompile Include="..\..\ext\miniupnpc\minisoap.c" />
- <ClCompile Include="..\..\ext\miniupnpc\minissdpc.c" />
- <ClCompile Include="..\..\ext\miniupnpc\miniupnpc.c" />
- <ClCompile Include="..\..\ext\miniupnpc\miniwget.c" />
- <ClCompile Include="..\..\ext\miniupnpc\minixml.c" />
- <ClCompile Include="..\..\ext\miniupnpc\portlistingparse.c" />
- <ClCompile Include="..\..\ext\miniupnpc\receivedata.c" />
- <ClCompile Include="..\..\ext\miniupnpc\upnpcommands.c" />
- <ClCompile Include="..\..\ext\miniupnpc\upnpdev.c" />
- <ClCompile Include="..\..\ext\miniupnpc\upnperrors.c" />
- <ClCompile Include="..\..\ext\miniupnpc\upnpreplyparse.c" />
- <ClCompile Include="..\..\node\C25519.cpp" />
- <ClCompile Include="..\..\node\Capability.cpp" />
- <ClCompile Include="..\..\node\CertificateOfMembership.cpp" />
- <ClCompile Include="..\..\node\CertificateOfOwnership.cpp" />
- <ClCompile Include="..\..\node\Cluster.cpp" />
- <ClCompile Include="..\..\node\Identity.cpp" />
- <ClCompile Include="..\..\node\IncomingPacket.cpp" />
- <ClCompile Include="..\..\node\InetAddress.cpp" />
- <ClCompile Include="..\..\node\Membership.cpp" />
- <ClCompile Include="..\..\node\Multicaster.cpp" />
- <ClCompile Include="..\..\node\Network.cpp" />
- <ClCompile Include="..\..\node\NetworkConfig.cpp" />
- <ClCompile Include="..\..\node\Node.cpp" />
- <ClCompile Include="..\..\node\OutboundMulticast.cpp" />
- <ClCompile Include="..\..\node\Packet.cpp" />
- <ClCompile Include="..\..\node\Path.cpp" />
- <ClCompile Include="..\..\node\Peer.cpp" />
- <ClCompile Include="..\..\node\Poly1305.cpp" />
- <ClCompile Include="..\..\node\Revocation.cpp" />
- <ClCompile Include="..\..\node\Salsa20.cpp" />
- <ClCompile Include="..\..\node\SelfAwareness.cpp" />
- <ClCompile Include="..\..\node\SHA512.cpp" />
- <ClCompile Include="..\..\node\Switch.cpp" />
- <ClCompile Include="..\..\node\Tag.cpp" />
- <ClCompile Include="..\..\node\Topology.cpp" />
- <ClCompile Include="..\..\node\Utils.cpp" />
- <ClCompile Include="..\..\one.cpp">
- <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
- <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</ExcludedFromBuild>
- <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</ExcludedFromBuild>
- <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">false</ExcludedFromBuild>
- <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
- <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Profile|x64'">false</ExcludedFromBuild>
- </ClCompile>
- <ClCompile Include="..\..\osdep\Http.cpp" />
- <ClCompile Include="..\..\osdep\ManagedRoute.cpp" />
- <ClCompile Include="..\..\osdep\OSUtils.cpp" />
- <ClCompile Include="..\..\osdep\PortMapper.cpp" />
- <ClCompile Include="..\..\osdep\WindowsEthernetTap.cpp" />
- <ClCompile Include="..\..\selftest.cpp">
- <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
- <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
- <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">true</ExcludedFromBuild>
- <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
- <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
- <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Profile|x64'">true</ExcludedFromBuild>
- </ClCompile>
- <ClCompile Include="..\..\service\OneService.cpp" />
- <ClCompile Include="..\..\service\SoftwareUpdater.cpp" />
- <ClCompile Include="ServiceBase.cpp" />
- <ClCompile Include="ServiceInstaller.cpp" />
- <ClCompile Include="ZeroTierOneService.cpp" />
- </ItemGroup>
- <ItemGroup>
- <ClInclude Include="..\..\controller\EmbeddedNetworkController.hpp" />
- <ClInclude Include="..\..\controller\JSONDB.hpp" />
- <ClInclude Include="..\..\ext\http-parser\http_parser.h" />
- <ClInclude Include="..\..\ext\json\json.hpp" />
- <ClInclude Include="..\..\ext\libnatpmp\getgateway.h" />
- <ClInclude Include="..\..\ext\libnatpmp\natpmp.h" />
- <ClInclude Include="..\..\ext\libnatpmp\wingettimeofday.h" />
- <ClInclude Include="..\..\ext\miniupnpc\codelength.h" />
- <ClInclude Include="..\..\ext\miniupnpc\connecthostport.h" />
- <ClInclude Include="..\..\ext\miniupnpc\igd_desc_parse.h" />
- <ClInclude Include="..\..\ext\miniupnpc\minisoap.h" />
- <ClInclude Include="..\..\ext\miniupnpc\minissdpc.h" />
- <ClInclude Include="..\..\ext\miniupnpc\miniupnpc.h" />
- <ClInclude Include="..\..\ext\miniupnpc\miniupnpctypes.h" />
- <ClInclude Include="..\..\ext\miniupnpc\miniupnpc_declspec.h" />
- <ClInclude Include="..\..\ext\miniupnpc\miniwget.h" />
- <ClInclude Include="..\..\ext\miniupnpc\minixml.h" />
- <ClInclude Include="..\..\ext\miniupnpc\portlistingparse.h" />
- <ClInclude Include="..\..\ext\miniupnpc\receivedata.h" />
- <ClInclude Include="..\..\ext\miniupnpc\upnpcommands.h" />
- <ClInclude Include="..\..\ext\miniupnpc\upnpdev.h" />
- <ClInclude Include="..\..\ext\miniupnpc\upnperrors.h" />
- <ClInclude Include="..\..\ext\miniupnpc\upnpreplyparse.h" />
- <ClInclude Include="..\..\ext\x64-salsa2012-asm\salsa2012.h" />
- <ClInclude Include="..\..\include\ZeroTierOne.h" />
- <ClInclude Include="..\..\node\Address.hpp" />
- <ClInclude Include="..\..\node\Array.hpp" />
- <ClInclude Include="..\..\node\AtomicCounter.hpp" />
- <ClInclude Include="..\..\node\BandwidthAccount.hpp" />
- <ClInclude Include="..\..\node\BinarySemaphore.hpp" />
- <ClInclude Include="..\..\node\Buffer.hpp" />
- <ClInclude Include="..\..\node\C25519.hpp" />
- <ClInclude Include="..\..\node\CertificateOfMembership.hpp" />
- <ClInclude Include="..\..\node\CertificateOfOwnership.hpp" />
- <ClInclude Include="..\..\node\Cluster.hpp" />
- <ClInclude Include="..\..\node\CMWC4096.hpp" />
- <ClInclude Include="..\..\node\Constants.hpp" />
- <ClInclude Include="..\..\node\Credential.hpp" />
- <ClInclude Include="..\..\node\DeferredPackets.hpp" />
- <ClInclude Include="..\..\node\Dictionary.hpp" />
- <ClInclude Include="..\..\node\Hashtable.hpp" />
- <ClInclude Include="..\..\node\Identity.hpp" />
- <ClInclude Include="..\..\node\IncomingPacket.hpp" />
- <ClInclude Include="..\..\node\InetAddress.hpp" />
- <ClInclude Include="..\..\node\MAC.hpp" />
- <ClInclude Include="..\..\node\Multicaster.hpp" />
- <ClInclude Include="..\..\node\MulticastGroup.hpp" />
- <ClInclude Include="..\..\node\Mutex.hpp" />
- <ClInclude Include="..\..\node\Network.hpp" />
- <ClInclude Include="..\..\node\NetworkConfig.hpp" />
- <ClInclude Include="..\..\node\NetworkController.hpp" />
- <ClInclude Include="..\..\node\Node.hpp" />
- <ClInclude Include="..\..\node\NonCopyable.hpp" />
- <ClInclude Include="..\..\node\OutboundMulticast.hpp" />
- <ClInclude Include="..\..\node\Packet.hpp" />
- <ClInclude Include="..\..\node\Path.hpp" />
- <ClInclude Include="..\..\node\Peer.hpp" />
- <ClInclude Include="..\..\node\Poly1305.hpp" />
- <ClInclude Include="..\..\node\RuntimeEnvironment.hpp" />
- <ClInclude Include="..\..\node\Salsa20.hpp" />
- <ClInclude Include="..\..\node\SelfAwareness.hpp" />
- <ClInclude Include="..\..\node\SHA512.hpp" />
- <ClInclude Include="..\..\node\SharedPtr.hpp" />
- <ClInclude Include="..\..\node\Switch.hpp" />
- <ClInclude Include="..\..\node\Topology.hpp" />
- <ClInclude Include="..\..\node\Utils.hpp" />
- <ClInclude Include="..\..\node\World.hpp" />
- <ClInclude Include="..\..\osdep\Binder.hpp" />
- <ClInclude Include="..\..\osdep\Http.hpp" />
- <ClInclude Include="..\..\osdep\ManagedRoute.hpp" />
- <ClInclude Include="..\..\osdep\OSUtils.hpp" />
- <ClInclude Include="..\..\osdep\Phy.hpp" />
- <ClInclude Include="..\..\osdep\PortMapper.hpp" />
- <ClInclude Include="..\..\osdep\Thread.hpp" />
- <ClInclude Include="..\..\osdep\WindowsEthernetTap.hpp" />
- <ClInclude Include="..\..\service\OneService.hpp" />
- <ClInclude Include="..\..\service\SoftwareUpdater.hpp" />
- <ClInclude Include="..\..\version.h" />
- <ClInclude Include="resource.h" />
- <ClInclude Include="ServiceBase.h" />
- <ClInclude Include="ServiceInstaller.h" />
- <ClInclude Include="ZeroTierOneService.h" />
- </ItemGroup>
- <ItemGroup>
- <ResourceCompile Include="ZeroTierOne.rc" />
- </ItemGroup>
- <PropertyGroup Label="Globals">
- <ProjectGuid>{B00A4957-5977-4AC1-9EF4-571DC27EADA2}</ProjectGuid>
- <RootNamespace>ZeroTierOne</RootNamespace>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>true</UseDebugLibraries>
- <PlatformToolset>v140</PlatformToolset>
- <CharacterSet>MultiByte</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>true</UseDebugLibraries>
- <PlatformToolset>v140</PlatformToolset>
- <CharacterSet>MultiByte</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>true</UseDebugLibraries>
- <PlatformToolset>v140</PlatformToolset>
- <CharacterSet>MultiByte</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile|x64'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>true</UseDebugLibraries>
- <PlatformToolset>v140</PlatformToolset>
- <CharacterSet>MultiByte</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>false</UseDebugLibraries>
- <PlatformToolset>v140</PlatformToolset>
- <WholeProgramOptimization>true</WholeProgramOptimization>
- <CharacterSet>MultiByte</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>false</UseDebugLibraries>
- <PlatformToolset>v140</PlatformToolset>
- <WholeProgramOptimization>true</WholeProgramOptimization>
- <CharacterSet>MultiByte</CharacterSet>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
- <ImportGroup Label="ExtensionSettings">
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'" Label="PropertySheets">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Profile|x64'" Label="PropertySheets">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <PropertyGroup Label="UserMacros" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <TargetExt>.exe</TargetExt>
- <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
- <TargetName>zerotier-one_x86</TargetName>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">
- <TargetExt>.exe</TargetExt>
- <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
- <TargetName>zerotier-one_x86</TargetName>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <TargetExt>.exe</TargetExt>
- <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
- <TargetName>zerotier-one_x86</TargetName>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
- <TargetExt>.exe</TargetExt>
- <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
- <TargetName>zerotier-one_x64</TargetName>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile|x64'">
- <TargetExt>.exe</TargetExt>
- <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
- <TargetName>zerotier-one_x64</TargetName>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
- <TargetExt>.exe</TargetExt>
- <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
- <TargetName>zerotier-one_x64</TargetName>
- </PropertyGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <ClCompile>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>Disabled</Optimization>
- <SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>
- </AdditionalIncludeDirectories>
- <PreprocessorDefinitions>NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <DisableSpecificWarnings>4996</DisableSpecificWarnings>
- </ClCompile>
- <Link>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
- <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">
- <ClCompile>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>Disabled</Optimization>
- <SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>
- </AdditionalIncludeDirectories>
- <PreprocessorDefinitions>NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <DisableSpecificWarnings>4996</DisableSpecificWarnings>
- </ClCompile>
- <Link>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
- <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
- <ClCompile>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>Disabled</Optimization>
- <SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>
- </AdditionalIncludeDirectories>
- <PreprocessorDefinitions>NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_RULES_ENGINE_DEBUGGING;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <MultiProcessorCompilation>false</MultiProcessorCompilation>
- <DisableSpecificWarnings>4996</DisableSpecificWarnings>
- </ClCompile>
- <Link>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
- <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
- <AdditionalOptions>"notelemetry.obj" %(AdditionalOptions)</AdditionalOptions>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Profile|x64'">
- <ClCompile>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>Disabled</Optimization>
- <SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>
- </AdditionalIncludeDirectories>
- <PreprocessorDefinitions>NOMINMAX;STATICLIB;WIN32;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <MultiProcessorCompilation>false</MultiProcessorCompilation>
- <DisableSpecificWarnings>4996</DisableSpecificWarnings>
- </ClCompile>
- <Link>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
- <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
- <AdditionalOptions>"notelemetry.obj" %(AdditionalOptions)</AdditionalOptions>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <ClCompile>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>MaxSpeed</Optimization>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <IntrinsicFunctions>true</IntrinsicFunctions>
- <SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>
- </AdditionalIncludeDirectories>
- <PreprocessorDefinitions>STATICLIB;ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;WIN32;NOMINMAX;ZT_SOFTWARE_UPDATE_DEFAULT="apply";ZT_BUILD_PLATFORM=2;ZT_BUILD_ARCHITECTURE=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
- <EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
- <StringPooling>true</StringPooling>
- <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
- <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
- <OmitFramePointers>true</OmitFramePointers>
- <DisableSpecificWarnings>4996</DisableSpecificWarnings>
- <ControlFlowGuard>Guard</ControlFlowGuard>
- </ClCompile>
- <Link>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <EnableCOMDATFolding>true</EnableCOMDATFolding>
- <OptimizeReferences>true</OptimizeReferences>
- <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
- <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
- <ClCompile>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>MaxSpeed</Optimization>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <IntrinsicFunctions>true</IntrinsicFunctions>
- <SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>
- </AdditionalIncludeDirectories>
- <PreprocessorDefinitions>STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="apply";ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;WIN32;NOMINMAX;ZT_BUILD_PLATFORM=2;ZT_BUILD_ARCHITECTURE=2;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
- <EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
- <StringPooling>true</StringPooling>
- <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
- <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
- <OmitFramePointers>true</OmitFramePointers>
- <DisableSpecificWarnings>4996</DisableSpecificWarnings>
- <ControlFlowGuard>Guard</ControlFlowGuard>
- <EnableParallelCodeGeneration>false</EnableParallelCodeGeneration>
- <CallingConvention>VectorCall</CallingConvention>
- </ClCompile>
- <Link>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <EnableCOMDATFolding>true</EnableCOMDATFolding>
- <OptimizeReferences>true</OptimizeReferences>
- <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
- <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
- </Link>
- </ItemDefinitionGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
- <ImportGroup Label="ExtensionTargets">
- </ImportGroup>
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Profile|Win32">
+ <Configuration>Profile</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Profile|x64">
+ <Configuration>Profile</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\controller\DB.cpp" />
+ <ClCompile Include="..\..\controller\EmbeddedNetworkController.cpp" />
+ <ClCompile Include="..\..\controller\FileDB.cpp" />
+ <ClCompile Include="..\..\controller\RethinkDB.cpp" />
+ <ClCompile Include="..\..\ext\http-parser\http_parser.c" />
+ <ClCompile Include="..\..\ext\libnatpmp\getgateway.c" />
+ <ClCompile Include="..\..\ext\libnatpmp\natpmp.c" />
+ <ClCompile Include="..\..\ext\libnatpmp\wingettimeofday.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\connecthostport.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\igd_desc_parse.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\minisoap.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\minissdpc.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\miniupnpc.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\miniwget.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\minixml.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\portlistingparse.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\receivedata.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\upnpcommands.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\upnpdev.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\upnperrors.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\upnpreplyparse.c" />
+ <ClCompile Include="..\..\node\C25519.cpp" />
+ <ClCompile Include="..\..\node\Capability.cpp" />
+ <ClCompile Include="..\..\node\CertificateOfMembership.cpp" />
+ <ClCompile Include="..\..\node\CertificateOfOwnership.cpp" />
+ <ClCompile Include="..\..\node\Identity.cpp" />
+ <ClCompile Include="..\..\node\IncomingPacket.cpp" />
+ <ClCompile Include="..\..\node\InetAddress.cpp" />
+ <ClCompile Include="..\..\node\Membership.cpp" />
+ <ClCompile Include="..\..\node\Multicaster.cpp" />
+ <ClCompile Include="..\..\node\Network.cpp" />
+ <ClCompile Include="..\..\node\NetworkConfig.cpp" />
+ <ClCompile Include="..\..\node\Node.cpp" />
+ <ClCompile Include="..\..\node\OutboundMulticast.cpp" />
+ <ClCompile Include="..\..\node\Packet.cpp" />
+ <ClCompile Include="..\..\node\Path.cpp" />
+ <ClCompile Include="..\..\node\Peer.cpp" />
+ <ClCompile Include="..\..\node\Poly1305.cpp" />
+ <ClCompile Include="..\..\node\Revocation.cpp" />
+ <ClCompile Include="..\..\node\Salsa20.cpp" />
+ <ClCompile Include="..\..\node\SelfAwareness.cpp" />
+ <ClCompile Include="..\..\node\SHA512.cpp" />
+ <ClCompile Include="..\..\node\Switch.cpp" />
+ <ClCompile Include="..\..\node\Tag.cpp" />
+ <ClCompile Include="..\..\node\Topology.cpp" />
+ <ClCompile Include="..\..\node\Trace.cpp" />
+ <ClCompile Include="..\..\node\Utils.cpp" />
+ <ClCompile Include="..\..\one.cpp">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Profile|x64'">false</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="..\..\osdep\Http.cpp" />
+ <ClCompile Include="..\..\osdep\ManagedRoute.cpp" />
+ <ClCompile Include="..\..\osdep\OSUtils.cpp" />
+ <ClCompile Include="..\..\osdep\PortMapper.cpp" />
+ <ClCompile Include="..\..\osdep\WindowsEthernetTap.cpp" />
+ <ClCompile Include="..\..\selftest.cpp">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Profile|x64'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="..\..\service\OneService.cpp" />
+ <ClCompile Include="..\..\service\SoftwareUpdater.cpp" />
+ <ClCompile Include="ServiceBase.cpp" />
+ <ClCompile Include="ServiceInstaller.cpp" />
+ <ClCompile Include="ZeroTierOneService.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\controller\DB.hpp" />
+ <ClInclude Include="..\..\controller\EmbeddedNetworkController.hpp" />
+ <ClInclude Include="..\..\controller\FileDB.hpp" />
+ <ClInclude Include="..\..\controller\RethinkDB.hpp" />
+ <ClInclude Include="..\..\ext\http-parser\http_parser.h" />
+ <ClInclude Include="..\..\ext\json\json.hpp" />
+ <ClInclude Include="..\..\ext\libnatpmp\getgateway.h" />
+ <ClInclude Include="..\..\ext\libnatpmp\natpmp.h" />
+ <ClInclude Include="..\..\ext\libnatpmp\wingettimeofday.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\codelength.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\connecthostport.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\igd_desc_parse.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\minisoap.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\minissdpc.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\miniupnpc.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\miniupnpctypes.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\miniupnpc_declspec.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\miniwget.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\minixml.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\portlistingparse.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\receivedata.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\upnpcommands.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\upnpdev.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\upnperrors.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\upnpreplyparse.h" />
+ <ClInclude Include="..\..\ext\x64-salsa2012-asm\salsa2012.h" />
+ <ClInclude Include="..\..\include\ZeroTierOne.h" />
+ <ClInclude Include="..\..\node\Address.hpp" />
+ <ClInclude Include="..\..\node\AtomicCounter.hpp" />
+ <ClInclude Include="..\..\node\Buffer.hpp" />
+ <ClInclude Include="..\..\node\C25519.hpp" />
+ <ClInclude Include="..\..\node\CertificateOfMembership.hpp" />
+ <ClInclude Include="..\..\node\CertificateOfOwnership.hpp" />
+ <ClInclude Include="..\..\node\Constants.hpp" />
+ <ClInclude Include="..\..\node\Credential.hpp" />
+ <ClInclude Include="..\..\node\Dictionary.hpp" />
+ <ClInclude Include="..\..\node\Hashtable.hpp" />
+ <ClInclude Include="..\..\node\Identity.hpp" />
+ <ClInclude Include="..\..\node\IncomingPacket.hpp" />
+ <ClInclude Include="..\..\node\InetAddress.hpp" />
+ <ClInclude Include="..\..\node\MAC.hpp" />
+ <ClInclude Include="..\..\node\Multicaster.hpp" />
+ <ClInclude Include="..\..\node\MulticastGroup.hpp" />
+ <ClInclude Include="..\..\node\Mutex.hpp" />
+ <ClInclude Include="..\..\node\Network.hpp" />
+ <ClInclude Include="..\..\node\NetworkConfig.hpp" />
+ <ClInclude Include="..\..\node\NetworkController.hpp" />
+ <ClInclude Include="..\..\node\Node.hpp" />
+ <ClInclude Include="..\..\node\OutboundMulticast.hpp" />
+ <ClInclude Include="..\..\node\Packet.hpp" />
+ <ClInclude Include="..\..\node\Path.hpp" />
+ <ClInclude Include="..\..\node\Peer.hpp" />
+ <ClInclude Include="..\..\node\Poly1305.hpp" />
+ <ClInclude Include="..\..\node\RuntimeEnvironment.hpp" />
+ <ClInclude Include="..\..\node\Salsa20.hpp" />
+ <ClInclude Include="..\..\node\SelfAwareness.hpp" />
+ <ClInclude Include="..\..\node\SHA512.hpp" />
+ <ClInclude Include="..\..\node\SharedPtr.hpp" />
+ <ClInclude Include="..\..\node\Switch.hpp" />
+ <ClInclude Include="..\..\node\Topology.hpp" />
+ <ClInclude Include="..\..\node\Trace.hpp" />
+ <ClInclude Include="..\..\node\Utils.hpp" />
+ <ClInclude Include="..\..\node\World.hpp" />
+ <ClInclude Include="..\..\osdep\Binder.hpp" />
+ <ClInclude Include="..\..\osdep\Http.hpp" />
+ <ClInclude Include="..\..\osdep\ManagedRoute.hpp" />
+ <ClInclude Include="..\..\osdep\OSUtils.hpp" />
+ <ClInclude Include="..\..\osdep\Phy.hpp" />
+ <ClInclude Include="..\..\osdep\PortMapper.hpp" />
+ <ClInclude Include="..\..\osdep\Thread.hpp" />
+ <ClInclude Include="..\..\osdep\WindowsEthernetTap.hpp" />
+ <ClInclude Include="..\..\service\OneService.hpp" />
+ <ClInclude Include="..\..\service\SoftwareUpdater.hpp" />
+ <ClInclude Include="..\..\version.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="ServiceBase.h" />
+ <ClInclude Include="ServiceInstaller.h" />
+ <ClInclude Include="ZeroTierOneService.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="ZeroTierOne.rc" />
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{B00A4957-5977-4AC1-9EF4-571DC27EADA2}</ProjectGuid>
+ <RootNamespace>ZeroTierOne</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Profile|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetExt>.exe</TargetExt>
+ <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>zerotier-one_x86</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">
+ <TargetExt>.exe</TargetExt>
+ <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>zerotier-one_x86</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetExt>.exe</TargetExt>
+ <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>zerotier-one_x86</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetExt>.exe</TargetExt>
+ <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>zerotier-one_x64</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile|x64'">
+ <TargetExt>.exe</TargetExt>
+ <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>zerotier-one_x64</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetExt>.exe</TargetExt>
+ <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>zerotier-one_x64</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>
+ </AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>ZT_EXPORT;NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <DisableSpecificWarnings>4996</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>
+ </AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>ZT_EXPORT;NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <DisableSpecificWarnings>4996</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>
+ </AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>ZT_EXPORT;NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_RULES_ENGINE_DEBUGGING;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MultiProcessorCompilation>false</MultiProcessorCompilation>
+ <DisableSpecificWarnings>4996</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalOptions>"notelemetry.obj" %(AdditionalOptions)</AdditionalOptions>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Profile|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>
+ </AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>ZT_EXPORT;NOMINMAX;STATICLIB;WIN32;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MultiProcessorCompilation>false</MultiProcessorCompilation>
+ <DisableSpecificWarnings>4996</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalOptions>"notelemetry.obj" %(AdditionalOptions)</AdditionalOptions>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>
+ </AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>ZT_EXPORT;STATICLIB;ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;WIN32;NOMINMAX;ZT_SOFTWARE_UPDATE_DEFAULT="apply";ZT_BUILD_PLATFORM=2;ZT_BUILD_ARCHITECTURE=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
+ <StringPooling>true</StringPooling>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <DisableSpecificWarnings>4996</DisableSpecificWarnings>
+ <ControlFlowGuard>Guard</ControlFlowGuard>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>
+ </AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>ZT_EXPORT;STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="apply";ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;WIN32;NOMINMAX;ZT_BUILD_PLATFORM=2;ZT_BUILD_ARCHITECTURE=2;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
+ <StringPooling>true</StringPooling>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <DisableSpecificWarnings>4996</DisableSpecificWarnings>
+ <ControlFlowGuard>Guard</ControlFlowGuard>
+ <EnableParallelCodeGeneration>false</EnableParallelCodeGeneration>
+ <CallingConvention>VectorCall</CallingConvention>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
</Project> \ No newline at end of file
diff --git a/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters b/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters
index cca10f97..d07c0638 100644
--- a/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters
+++ b/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters
@@ -1,512 +1,506 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <ItemGroup>
- <Filter Include="Source Files">
- <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
- <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
- </Filter>
- <Filter Include="Header Files">
- <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
- <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
- </Filter>
- <Filter Include="Resource Files">
- <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
- <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
- </Filter>
- <Filter Include="Source Files\node">
- <UniqueIdentifier>{67b1c0f8-b018-4169-9c14-7032ed12c786}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\include">
- <UniqueIdentifier>{40761a4c-e8db-4a91-9cab-7afef332f4a8}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\node">
- <UniqueIdentifier>{da3b8126-840c-45db-8abe-9d7e7976f8be}</UniqueIdentifier>
- </Filter>
- <Filter Include="Source Files\osdep">
- <UniqueIdentifier>{6054dfae-4ed2-4d69-8cf5-d6f27646f2d7}</UniqueIdentifier>
- </Filter>
- <Filter Include="Source Files\service">
- <UniqueIdentifier>{9944293a-4a1a-40e9-b92a-eff31fe87e2c}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\osdep">
- <UniqueIdentifier>{ca21bd6b-ff4e-4f9e-bedd-c9f603d2d0d6}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\service">
- <UniqueIdentifier>{e1743b3c-1d18-47f1-ab5a-f5703c19f1df}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\ext">
- <UniqueIdentifier>{71865460-d693-4c73-84f6-dbff42f49df6}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\ext\http-parser">
- <UniqueIdentifier>{17ae9a01-d39f-4c6d-a800-8f2cd0804c96}</UniqueIdentifier>
- </Filter>
- <Filter Include="Source Files\ext">
- <UniqueIdentifier>{7784af31-5b60-4300-b07e-44cf864c54db}</UniqueIdentifier>
- </Filter>
- <Filter Include="Source Files\ext\http-parser">
- <UniqueIdentifier>{f8a1c208-15b8-4d85-a4cb-11d2b82f2d1e}</UniqueIdentifier>
- </Filter>
- <Filter Include="Source Files\windows">
- <UniqueIdentifier>{43f75f84-c70d-4d44-a0ef-28a7a399abd4}</UniqueIdentifier>
- </Filter>
- <Filter Include="Source Files\windows\ZeroTierOne">
- <UniqueIdentifier>{0da07a2f-8922-4827-ac51-29ca3f30f881}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\windows">
- <UniqueIdentifier>{b74916eb-bb6c-4449-a2a2-fa0b17f60121}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\windows\ZeroTierOne">
- <UniqueIdentifier>{bf604491-14c4-4a74-81a6-6105d07c5c7c}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\ext\miniupnpc">
- <UniqueIdentifier>{5423fb64-896b-432e-a19d-88d4467f89f9}</UniqueIdentifier>
- </Filter>
- <Filter Include="Source Files\ext\miniupnpc">
- <UniqueIdentifier>{56cc3ab8-3336-4a22-9471-c267ee46cd54}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\ext\libnatpmp">
- <UniqueIdentifier>{d7292d0d-72a0-4ed6-b717-21debb120737}</UniqueIdentifier>
- </Filter>
- <Filter Include="Source Files\ext\libnatpmp">
- <UniqueIdentifier>{409ec37e-ff36-4c13-b18d-52d6052e0ca2}</UniqueIdentifier>
- </Filter>
- <Filter Include="Source Files\controller">
- <UniqueIdentifier>{3cad34c8-c436-43ae-8323-57803637c832}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\ext\json">
- <UniqueIdentifier>{ff20532b-d9a2-440d-a7b4-b49e26a9b2f8}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\ext\x64-salsa2012-asm">
- <UniqueIdentifier>{05d9cde8-03ae-4e37-b9f7-7417de98cbe9}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\controller">
- <UniqueIdentifier>{7dc22e9c-f869-41e7-b43d-f07f5b94f6fb}</UniqueIdentifier>
- </Filter>
- </ItemGroup>
- <ItemGroup>
- <ClCompile Include="..\..\service\OneService.cpp">
- <Filter>Source Files\service</Filter>
- </ClCompile>
- <ClCompile Include="..\..\osdep\WindowsEthernetTap.cpp">
- <Filter>Source Files\osdep</Filter>
- </ClCompile>
- <ClCompile Include="..\..\osdep\Http.cpp">
- <Filter>Source Files\osdep</Filter>
- </ClCompile>
- <ClCompile Include="..\..\osdep\OSUtils.cpp">
- <Filter>Source Files\osdep</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\C25519.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\CertificateOfMembership.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Identity.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\IncomingPacket.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\InetAddress.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Multicaster.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Network.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\NetworkConfig.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Node.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\OutboundMulticast.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Packet.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Peer.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Poly1305.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Salsa20.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\SelfAwareness.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\SHA512.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Switch.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Topology.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Utils.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\http-parser\http_parser.c">
- <Filter>Source Files\ext\http-parser</Filter>
- </ClCompile>
- <ClCompile Include="ServiceBase.cpp">
- <Filter>Source Files\windows\ZeroTierOne</Filter>
- </ClCompile>
- <ClCompile Include="ServiceInstaller.cpp">
- <Filter>Source Files\windows\ZeroTierOne</Filter>
- </ClCompile>
- <ClCompile Include="ZeroTierOneService.cpp">
- <Filter>Source Files\windows\ZeroTierOne</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Path.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Cluster.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\connecthostport.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\igd_desc_parse.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\minisoap.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\minissdpc.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\miniupnpc.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\miniwget.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\minixml.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\portlistingparse.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\receivedata.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\upnpcommands.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\upnpdev.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\upnperrors.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\upnpreplyparse.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\osdep\PortMapper.cpp">
- <Filter>Source Files\osdep</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\libnatpmp\getgateway.c">
- <Filter>Source Files\ext\libnatpmp</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\libnatpmp\natpmp.c">
- <Filter>Source Files\ext\libnatpmp</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\libnatpmp\wingettimeofday.c">
- <Filter>Source Files\ext\libnatpmp</Filter>
- </ClCompile>
- <ClCompile Include="..\..\osdep\ManagedRoute.cpp">
- <Filter>Source Files\osdep</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Membership.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Capability.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Revocation.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Tag.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\controller\EmbeddedNetworkController.cpp">
- <Filter>Source Files\controller</Filter>
- </ClCompile>
- <ClCompile Include="..\..\controller\JSONDB.cpp">
- <Filter>Source Files\controller</Filter>
- </ClCompile>
- <ClCompile Include="..\..\service\SoftwareUpdater.cpp">
- <Filter>Source Files\service</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\CertificateOfOwnership.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\one.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- <ClCompile Include="..\..\selftest.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- </ItemGroup>
- <ItemGroup>
- <ClInclude Include="resource.h">
- <Filter>Header Files</Filter>
- </ClInclude>
- <ClInclude Include="..\..\version.h">
- <Filter>Header Files</Filter>
- </ClInclude>
- <ClInclude Include="..\..\include\ZeroTierOne.h">
- <Filter>Header Files\include</Filter>
- </ClInclude>
- <ClInclude Include="..\..\osdep\Http.hpp">
- <Filter>Header Files\osdep</Filter>
- </ClInclude>
- <ClInclude Include="..\..\osdep\OSUtils.hpp">
- <Filter>Header Files\osdep</Filter>
- </ClInclude>
- <ClInclude Include="..\..\osdep\Phy.hpp">
- <Filter>Header Files\osdep</Filter>
- </ClInclude>
- <ClInclude Include="..\..\osdep\Thread.hpp">
- <Filter>Header Files\osdep</Filter>
- </ClInclude>
- <ClInclude Include="..\..\osdep\WindowsEthernetTap.hpp">
- <Filter>Header Files\osdep</Filter>
- </ClInclude>
- <ClInclude Include="..\..\service\OneService.hpp">
- <Filter>Header Files\service</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Address.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Array.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\AtomicCounter.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\BandwidthAccount.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Buffer.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\C25519.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\CertificateOfMembership.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\CMWC4096.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Constants.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Dictionary.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Identity.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\IncomingPacket.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\InetAddress.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\MAC.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Multicaster.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\MulticastGroup.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Mutex.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Network.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\NetworkConfig.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\NetworkController.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Node.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\NonCopyable.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\OutboundMulticast.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Packet.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Path.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Peer.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Poly1305.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\RuntimeEnvironment.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Salsa20.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\SelfAwareness.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\SHA512.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\SharedPtr.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Switch.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Topology.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Utils.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\http-parser\http_parser.h">
- <Filter>Header Files\ext\http-parser</Filter>
- </ClInclude>
- <ClInclude Include="ServiceBase.h">
- <Filter>Header Files\windows\ZeroTierOne</Filter>
- </ClInclude>
- <ClInclude Include="ServiceInstaller.h">
- <Filter>Header Files\windows\ZeroTierOne</Filter>
- </ClInclude>
- <ClInclude Include="ZeroTierOneService.h">
- <Filter>Header Files\windows\ZeroTierOne</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\BinarySemaphore.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Cluster.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Hashtable.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\DeferredPackets.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\World.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\codelength.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\connecthostport.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\igd_desc_parse.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\minisoap.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\minissdpc.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\miniupnpc.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\miniupnpc_declspec.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\miniupnpctypes.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\miniwget.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\minixml.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\portlistingparse.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\receivedata.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\upnpcommands.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\upnpdev.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\upnperrors.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\upnpreplyparse.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\osdep\PortMapper.hpp">
- <Filter>Header Files\osdep</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\libnatpmp\getgateway.h">
- <Filter>Header Files\ext\libnatpmp</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\libnatpmp\natpmp.h">
- <Filter>Header Files\ext\libnatpmp</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\libnatpmp\wingettimeofday.h">
- <Filter>Header Files\ext\libnatpmp</Filter>
- </ClInclude>
- <ClInclude Include="..\..\osdep\Binder.hpp">
- <Filter>Header Files\osdep</Filter>
- </ClInclude>
- <ClInclude Include="..\..\osdep\ManagedRoute.hpp">
- <Filter>Header Files\osdep</Filter>
- </ClInclude>
- <ClInclude Include="..\..\service\SoftwareUpdater.hpp">
- <Filter>Header Files\service</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\json\json.hpp">
- <Filter>Header Files\ext\json</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\CertificateOfOwnership.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Credential.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\x64-salsa2012-asm\salsa2012.h">
- <Filter>Header Files\ext\x64-salsa2012-asm</Filter>
- </ClInclude>
- <ClInclude Include="..\..\controller\EmbeddedNetworkController.hpp">
- <Filter>Header Files\controller</Filter>
- </ClInclude>
- <ClInclude Include="..\..\controller\JSONDB.hpp">
- <Filter>Header Files\controller</Filter>
- </ClInclude>
- </ItemGroup>
- <ItemGroup>
- <ResourceCompile Include="ZeroTierOne.rc">
- <Filter>Resource Files</Filter>
- </ResourceCompile>
- </ItemGroup>
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ <Filter Include="Source Files\node">
+ <UniqueIdentifier>{67b1c0f8-b018-4169-9c14-7032ed12c786}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\include">
+ <UniqueIdentifier>{40761a4c-e8db-4a91-9cab-7afef332f4a8}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\node">
+ <UniqueIdentifier>{da3b8126-840c-45db-8abe-9d7e7976f8be}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\osdep">
+ <UniqueIdentifier>{6054dfae-4ed2-4d69-8cf5-d6f27646f2d7}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\service">
+ <UniqueIdentifier>{9944293a-4a1a-40e9-b92a-eff31fe87e2c}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\osdep">
+ <UniqueIdentifier>{ca21bd6b-ff4e-4f9e-bedd-c9f603d2d0d6}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\service">
+ <UniqueIdentifier>{e1743b3c-1d18-47f1-ab5a-f5703c19f1df}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\ext">
+ <UniqueIdentifier>{71865460-d693-4c73-84f6-dbff42f49df6}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\ext\http-parser">
+ <UniqueIdentifier>{17ae9a01-d39f-4c6d-a800-8f2cd0804c96}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\ext">
+ <UniqueIdentifier>{7784af31-5b60-4300-b07e-44cf864c54db}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\ext\http-parser">
+ <UniqueIdentifier>{f8a1c208-15b8-4d85-a4cb-11d2b82f2d1e}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\windows">
+ <UniqueIdentifier>{43f75f84-c70d-4d44-a0ef-28a7a399abd4}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\windows\ZeroTierOne">
+ <UniqueIdentifier>{0da07a2f-8922-4827-ac51-29ca3f30f881}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\windows">
+ <UniqueIdentifier>{b74916eb-bb6c-4449-a2a2-fa0b17f60121}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\windows\ZeroTierOne">
+ <UniqueIdentifier>{bf604491-14c4-4a74-81a6-6105d07c5c7c}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\ext\miniupnpc">
+ <UniqueIdentifier>{5423fb64-896b-432e-a19d-88d4467f89f9}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\ext\miniupnpc">
+ <UniqueIdentifier>{56cc3ab8-3336-4a22-9471-c267ee46cd54}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\ext\libnatpmp">
+ <UniqueIdentifier>{d7292d0d-72a0-4ed6-b717-21debb120737}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\ext\libnatpmp">
+ <UniqueIdentifier>{409ec37e-ff36-4c13-b18d-52d6052e0ca2}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\controller">
+ <UniqueIdentifier>{3cad34c8-c436-43ae-8323-57803637c832}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\ext\json">
+ <UniqueIdentifier>{ff20532b-d9a2-440d-a7b4-b49e26a9b2f8}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\ext\x64-salsa2012-asm">
+ <UniqueIdentifier>{05d9cde8-03ae-4e37-b9f7-7417de98cbe9}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\controller">
+ <UniqueIdentifier>{7dc22e9c-f869-41e7-b43d-f07f5b94f6fb}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\service\OneService.cpp">
+ <Filter>Source Files\service</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\osdep\WindowsEthernetTap.cpp">
+ <Filter>Source Files\osdep</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\osdep\Http.cpp">
+ <Filter>Source Files\osdep</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\osdep\OSUtils.cpp">
+ <Filter>Source Files\osdep</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\C25519.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\CertificateOfMembership.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Identity.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\IncomingPacket.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\InetAddress.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Multicaster.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Network.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\NetworkConfig.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Node.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\OutboundMulticast.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Packet.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Peer.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Poly1305.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Salsa20.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\SelfAwareness.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\SHA512.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Switch.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Topology.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Utils.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\http-parser\http_parser.c">
+ <Filter>Source Files\ext\http-parser</Filter>
+ </ClCompile>
+ <ClCompile Include="ServiceBase.cpp">
+ <Filter>Source Files\windows\ZeroTierOne</Filter>
+ </ClCompile>
+ <ClCompile Include="ServiceInstaller.cpp">
+ <Filter>Source Files\windows\ZeroTierOne</Filter>
+ </ClCompile>
+ <ClCompile Include="ZeroTierOneService.cpp">
+ <Filter>Source Files\windows\ZeroTierOne</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Path.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\connecthostport.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\igd_desc_parse.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\minisoap.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\minissdpc.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\miniupnpc.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\miniwget.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\minixml.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\portlistingparse.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\receivedata.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\upnpcommands.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\upnpdev.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\upnperrors.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\upnpreplyparse.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\osdep\PortMapper.cpp">
+ <Filter>Source Files\osdep</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\libnatpmp\getgateway.c">
+ <Filter>Source Files\ext\libnatpmp</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\libnatpmp\natpmp.c">
+ <Filter>Source Files\ext\libnatpmp</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\libnatpmp\wingettimeofday.c">
+ <Filter>Source Files\ext\libnatpmp</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\osdep\ManagedRoute.cpp">
+ <Filter>Source Files\osdep</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Membership.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Capability.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Revocation.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Tag.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\controller\EmbeddedNetworkController.cpp">
+ <Filter>Source Files\controller</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\service\SoftwareUpdater.cpp">
+ <Filter>Source Files\service</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\CertificateOfOwnership.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\one.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\selftest.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Trace.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\controller\DB.cpp">
+ <Filter>Source Files\controller</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\controller\FileDB.cpp">
+ <Filter>Source Files\controller</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\controller\RethinkDB.cpp">
+ <Filter>Source Files\controller</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\version.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\ZeroTierOne.h">
+ <Filter>Header Files\include</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\osdep\Http.hpp">
+ <Filter>Header Files\osdep</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\osdep\OSUtils.hpp">
+ <Filter>Header Files\osdep</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\osdep\Phy.hpp">
+ <Filter>Header Files\osdep</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\osdep\Thread.hpp">
+ <Filter>Header Files\osdep</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\osdep\WindowsEthernetTap.hpp">
+ <Filter>Header Files\osdep</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\service\OneService.hpp">
+ <Filter>Header Files\service</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Address.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\AtomicCounter.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Buffer.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\C25519.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\CertificateOfMembership.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Constants.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Dictionary.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Identity.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\IncomingPacket.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\InetAddress.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\MAC.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Multicaster.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\MulticastGroup.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Mutex.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Network.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\NetworkConfig.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\NetworkController.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Node.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\OutboundMulticast.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Packet.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Path.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Peer.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Poly1305.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\RuntimeEnvironment.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Salsa20.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\SelfAwareness.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\SHA512.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\SharedPtr.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Switch.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Topology.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Utils.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\http-parser\http_parser.h">
+ <Filter>Header Files\ext\http-parser</Filter>
+ </ClInclude>
+ <ClInclude Include="ServiceBase.h">
+ <Filter>Header Files\windows\ZeroTierOne</Filter>
+ </ClInclude>
+ <ClInclude Include="ServiceInstaller.h">
+ <Filter>Header Files\windows\ZeroTierOne</Filter>
+ </ClInclude>
+ <ClInclude Include="ZeroTierOneService.h">
+ <Filter>Header Files\windows\ZeroTierOne</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Hashtable.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\World.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\codelength.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\connecthostport.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\igd_desc_parse.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\minisoap.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\minissdpc.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\miniupnpc.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\miniupnpc_declspec.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\miniupnpctypes.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\miniwget.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\minixml.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\portlistingparse.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\receivedata.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\upnpcommands.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\upnpdev.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\upnperrors.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\upnpreplyparse.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\osdep\PortMapper.hpp">
+ <Filter>Header Files\osdep</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\libnatpmp\getgateway.h">
+ <Filter>Header Files\ext\libnatpmp</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\libnatpmp\natpmp.h">
+ <Filter>Header Files\ext\libnatpmp</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\libnatpmp\wingettimeofday.h">
+ <Filter>Header Files\ext\libnatpmp</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\osdep\Binder.hpp">
+ <Filter>Header Files\osdep</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\osdep\ManagedRoute.hpp">
+ <Filter>Header Files\osdep</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\service\SoftwareUpdater.hpp">
+ <Filter>Header Files\service</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\json\json.hpp">
+ <Filter>Header Files\ext\json</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\CertificateOfOwnership.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Credential.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\x64-salsa2012-asm\salsa2012.h">
+ <Filter>Header Files\ext\x64-salsa2012-asm</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\controller\EmbeddedNetworkController.hpp">
+ <Filter>Header Files\controller</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Trace.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\controller\DB.hpp">
+ <Filter>Header Files\controller</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\controller\FileDB.hpp">
+ <Filter>Header Files\controller</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\controller\RethinkDB.hpp">
+ <Filter>Header Files\controller</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="ZeroTierOne.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
</Project> \ No newline at end of file
diff --git a/windows/ZeroTierOneSDK.sln b/windows/ZeroTierOneSDK.sln
new file mode 100644
index 00000000..3c5c077a
--- /dev/null
+++ b/windows/ZeroTierOneSDK.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZeroTierOneSDK", "ZeroTierOneSDK\ZeroTierOneSDK.vcxproj", "{05E8E6B2-4067-4660-AD85-C8F6130FAFC5}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {05E8E6B2-4067-4660-AD85-C8F6130FAFC5}.Debug|x64.ActiveCfg = Debug|x64
+ {05E8E6B2-4067-4660-AD85-C8F6130FAFC5}.Debug|x64.Build.0 = Debug|x64
+ {05E8E6B2-4067-4660-AD85-C8F6130FAFC5}.Debug|x86.ActiveCfg = Debug|Win32
+ {05E8E6B2-4067-4660-AD85-C8F6130FAFC5}.Debug|x86.Build.0 = Debug|Win32
+ {05E8E6B2-4067-4660-AD85-C8F6130FAFC5}.Release|x64.ActiveCfg = Release|x64
+ {05E8E6B2-4067-4660-AD85-C8F6130FAFC5}.Release|x64.Build.0 = Release|x64
+ {05E8E6B2-4067-4660-AD85-C8F6130FAFC5}.Release|x86.ActiveCfg = Release|Win32
+ {05E8E6B2-4067-4660-AD85-C8F6130FAFC5}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/windows/ZeroTierOneSDK/ZeroTierOneSDK.vcxproj b/windows/ZeroTierOneSDK/ZeroTierOneSDK.vcxproj
new file mode 100644
index 00000000..bc719b6e
--- /dev/null
+++ b/windows/ZeroTierOneSDK/ZeroTierOneSDK.vcxproj
@@ -0,0 +1,257 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{05E8E6B2-4067-4660-AD85-C8F6130FAFC5}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>ZeroTierOneSDK</RootNamespace>
+ <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(Platform)\$(Configuration)\</IntDir>
+ <TargetName>ZeroTierOne_x86d</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>ZeroTierOne_x64d</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(Platform)\$(Configuration)\</IntDir>
+ <TargetName>ZeroTierOne_x86</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>ZeroTierOne_x64</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;NOMINMAX;ZT_EXPORT;ZT_TRACE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ImportLibrary>$(SolutionDir)lib\$(TargetName).lib</ImportLibrary>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;WIN32;NOMINMAX;ZT_EXPORT;ZT_TRACE;ZT_RULES_ENGINE_DEBUGGING_NO;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ImportLibrary>$(SolutionDir)lib\$(TargetName).lib</ImportLibrary>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;ZT_EXPORT;ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;WIN32;NOMINMAX;ZT_SOFTWARE_UPDATE_DEFAULT="apply";ZT_BUILD_PLATFORM=2;ZT_BUILD_ARCHITECTURE=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ImportLibrary>$(SolutionDir)lib\$(TargetName).lib</ImportLibrary>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;ZT_EXPORT;ZT_SOFTWARE_UPDATE_DEFAULT="apply";ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;NOMINMAX;ZT_BUILD_PLATFORM=2;ZT_BUILD_ARCHITECTURE=2;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ImportLibrary>$(SolutionDir)lib\$(TargetName).lib</ImportLibrary>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <Text Include="ReadMe.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\include\ZeroTierOne.h" />
+ <ClInclude Include="..\..\node\Address.hpp" />
+ <ClInclude Include="..\..\node\Array.hpp" />
+ <ClInclude Include="..\..\node\AtomicCounter.hpp" />
+ <ClInclude Include="..\..\node\Buffer.hpp" />
+ <ClInclude Include="..\..\node\C25519.hpp" />
+ <ClInclude Include="..\..\node\Capability.hpp" />
+ <ClInclude Include="..\..\node\CertificateOfMembership.hpp" />
+ <ClInclude Include="..\..\node\CertificateOfOwnership.hpp" />
+ <ClInclude Include="..\..\node\CertificateOfRepresentation.hpp" />
+ <ClInclude Include="..\..\node\Cluster.hpp" />
+ <ClInclude Include="..\..\node\Constants.hpp" />
+ <ClInclude Include="..\..\node\Credential.hpp" />
+ <ClInclude Include="..\..\node\Dictionary.hpp" />
+ <ClInclude Include="..\..\node\Hashtable.hpp" />
+ <ClInclude Include="..\..\node\Identity.hpp" />
+ <ClInclude Include="..\..\node\IncomingPacket.hpp" />
+ <ClInclude Include="..\..\node\InetAddress.hpp" />
+ <ClInclude Include="..\..\node\MAC.hpp" />
+ <ClInclude Include="..\..\node\Membership.hpp" />
+ <ClInclude Include="..\..\node\Multicaster.hpp" />
+ <ClInclude Include="..\..\node\MulticastGroup.hpp" />
+ <ClInclude Include="..\..\node\Mutex.hpp" />
+ <ClInclude Include="..\..\node\Network.hpp" />
+ <ClInclude Include="..\..\node\NetworkConfig.hpp" />
+ <ClInclude Include="..\..\node\NetworkController.hpp" />
+ <ClInclude Include="..\..\node\Node.hpp" />
+ <ClInclude Include="..\..\node\NonCopyable.hpp" />
+ <ClInclude Include="..\..\node\OutboundMulticast.hpp" />
+ <ClInclude Include="..\..\node\Packet.hpp" />
+ <ClInclude Include="..\..\node\Path.hpp" />
+ <ClInclude Include="..\..\node\Peer.hpp" />
+ <ClInclude Include="..\..\node\Poly1305.hpp" />
+ <ClInclude Include="..\..\node\Revocation.hpp" />
+ <ClInclude Include="..\..\node\RuntimeEnvironment.hpp" />
+ <ClInclude Include="..\..\node\Salsa20.hpp" />
+ <ClInclude Include="..\..\node\SelfAwareness.hpp" />
+ <ClInclude Include="..\..\node\SHA512.hpp" />
+ <ClInclude Include="..\..\node\SharedPtr.hpp" />
+ <ClInclude Include="..\..\node\Switch.hpp" />
+ <ClInclude Include="..\..\node\Tag.hpp" />
+ <ClInclude Include="..\..\node\Topology.hpp" />
+ <ClInclude Include="..\..\node\Utils.hpp" />
+ <ClInclude Include="..\..\node\World.hpp" />
+ <ClInclude Include="targetver.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\node\C25519.cpp" />
+ <ClCompile Include="..\..\node\Capability.cpp" />
+ <ClCompile Include="..\..\node\CertificateOfMembership.cpp" />
+ <ClCompile Include="..\..\node\CertificateOfOwnership.cpp" />
+ <ClCompile Include="..\..\node\Cluster.cpp" />
+ <ClCompile Include="..\..\node\Identity.cpp" />
+ <ClCompile Include="..\..\node\IncomingPacket.cpp" />
+ <ClCompile Include="..\..\node\InetAddress.cpp" />
+ <ClCompile Include="..\..\node\Membership.cpp" />
+ <ClCompile Include="..\..\node\Multicaster.cpp" />
+ <ClCompile Include="..\..\node\Network.cpp" />
+ <ClCompile Include="..\..\node\NetworkConfig.cpp" />
+ <ClCompile Include="..\..\node\Node.cpp" />
+ <ClCompile Include="..\..\node\OutboundMulticast.cpp" />
+ <ClCompile Include="..\..\node\Packet.cpp" />
+ <ClCompile Include="..\..\node\Path.cpp" />
+ <ClCompile Include="..\..\node\Peer.cpp" />
+ <ClCompile Include="..\..\node\Poly1305.cpp" />
+ <ClCompile Include="..\..\node\Revocation.cpp" />
+ <ClCompile Include="..\..\node\Salsa20.cpp" />
+ <ClCompile Include="..\..\node\SelfAwareness.cpp" />
+ <ClCompile Include="..\..\node\SHA512.cpp" />
+ <ClCompile Include="..\..\node\Switch.cpp" />
+ <ClCompile Include="..\..\node\Tag.cpp" />
+ <ClCompile Include="..\..\node\Topology.cpp" />
+ <ClCompile Include="..\..\node\Utils.cpp" />
+ <ClCompile Include="dllmain.cpp">
+ <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</CompileAsManaged>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</CompileAsManaged>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ </PrecompiledHeader>
+ <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</CompileAsManaged>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</CompileAsManaged>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ </PrecompiledHeader>
+ </ClCompile>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/windows/ZeroTierOneSDK/ZeroTierOneSDK.vcxproj.filters b/windows/ZeroTierOneSDK/ZeroTierOneSDK.vcxproj.filters
new file mode 100644
index 00000000..d7e947f3
--- /dev/null
+++ b/windows/ZeroTierOneSDK/ZeroTierOneSDK.vcxproj.filters
@@ -0,0 +1,240 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <Text Include="ReadMe.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="targetver.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\ZeroTierOne.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Node.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Utils.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Address.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Array.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\AtomicCounter.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Buffer.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\C25519.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Capability.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\CertificateOfMembership.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\CertificateOfOwnership.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\CertificateOfRepresentation.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Cluster.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Constants.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Credential.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Dictionary.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Hashtable.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Identity.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\IncomingPacket.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\InetAddress.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\MAC.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Membership.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Multicaster.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\MulticastGroup.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Mutex.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Network.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\NetworkConfig.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\NetworkController.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\NonCopyable.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\OutboundMulticast.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Packet.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Path.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Peer.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Poly1305.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Revocation.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\RuntimeEnvironment.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Salsa20.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\SelfAwareness.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\SHA512.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\SharedPtr.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Switch.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Tag.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Topology.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\World.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="dllmain.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Node.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Utils.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\C25519.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Capability.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\CertificateOfMembership.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\CertificateOfOwnership.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Cluster.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Identity.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\IncomingPacket.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\InetAddress.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Membership.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Multicaster.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Network.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\NetworkConfig.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\OutboundMulticast.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Packet.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Path.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Peer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Poly1305.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Revocation.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Salsa20.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\SelfAwareness.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\SHA512.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Switch.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Tag.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Topology.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/windows/ZeroTierOneSDK/dllmain.cpp b/windows/ZeroTierOneSDK/dllmain.cpp
new file mode 100644
index 00000000..e263848d
--- /dev/null
+++ b/windows/ZeroTierOneSDK/dllmain.cpp
@@ -0,0 +1,22 @@
+// dllmain.cpp : Defines the entry point for the DLL application.
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+// Windows Header Files:
+#include <windows.h>
+#include "targetver.h"
+
+BOOL APIENTRY DllMain( HMODULE hModule,
+ DWORD ul_reason_for_call,
+ LPVOID lpReserved
+ )
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
diff --git a/windows/ZeroTierOneSDK/targetver.h b/windows/ZeroTierOneSDK/targetver.h
new file mode 100644
index 00000000..90e767bf
--- /dev/null
+++ b/windows/ZeroTierOneSDK/targetver.h
@@ -0,0 +1,8 @@
+#pragma once
+
+// Including SDKDDKVer.h defines the highest available Windows platform.
+
+// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
+// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
+
+#include <SDKDDKVer.h>
diff --git a/zerotier-one.spec b/zerotier-one.spec
index 306a5bfd..2e78cba9 100644
--- a/zerotier-one.spec
+++ b/zerotier-one.spec
@@ -1,5 +1,5 @@
Name: zerotier-one
-Version: 1.2.4
+Version: 1.2.6
Release: 1%{?dist}
Summary: ZeroTier One network virtualization service